Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/alessiobacin_llmproxy.json
T

1 line
6.5 MiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "alessiobacin/llmproxy", "files": {"tests/cli.test.js": {"8c29c098b9e92b82f038f604a765a9c2212ebea4": {"t": 1785746673.4360309, "n": 232385, "h": "92c908848b2a9c5b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--vision\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test default runs full text inference for every configured provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-default-text-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-or-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"OpenRouter\" });\n\n let capturedBody = null;\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async (url, init) => {\n capturedBody = init?.body ? JSON.parse(init.body) : null;\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n async text() { return JSON.stringify({ type: \"message\", content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }] }); },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Test inferenza provider/i);\n assert.match(stdout.toString(), /OpenRouter \\(deepseek-v4-flash\\)/i);\n assert.match(stdout.toString(), /✅ PASS/i);\n // Verify text-only message (no image)\n assert.ok(capturedBody, \"should have sent a request body\");\n assert.equal(capturedBody.messages[0].content.length, 1, \"should send only text content, no image\");\n assert.equal(capturedBody.messages[0].content[0].type, \"text\");\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart force-recreates the Docker runtime even when the container is already running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"recreated\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\")), true);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\", \"--all\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\", \"--all\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "ed9622047c71c0a3e03a83986cb2f510fc26fe17": {"t": 1785766740.0662181, "n": 232385, "h": "92c908848b2a9c5b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--vision\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test default runs full text inference for every configured provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-default-text-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-or-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"OpenRouter\" });\n\n let capturedBody = null;\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async (url, init) => {\n capturedBody = init?.body ? JSON.parse(init.body) : null;\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n async text() { return JSON.stringify({ type: \"message\", content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }] }); },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Test inferenza provider/i);\n assert.match(stdout.toString(), /OpenRouter \\(deepseek-v4-flash\\)/i);\n assert.match(stdout.toString(), /✅ PASS/i);\n // Verify text-only message (no image)\n assert.ok(capturedBody, \"should have sent a request body\");\n assert.equal(capturedBody.messages[0].content.length, 1, \"should send only text content, no image\");\n assert.equal(capturedBody.messages[0].content[0].type, \"text\");\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart force-recreates the Docker runtime even when the container is already running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"recreated\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\")), true);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\", \"--all\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\", \"--all\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "438fdbe430a16f53377382c4cc45ec5171875711": {"t": 1785766742.1859396, "n": 232367, "h": "fd6cb0ecb27f9bfd", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--vision\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test default runs full text inference for every configured provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-default-text-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-or-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"OpenRouter\" });\n\n let capturedBody = null;\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async (url, init) => {\n capturedBody = init?.body ? JSON.parse(init.body) : null;\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n async text() { return JSON.stringify({ type: \"message\", content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }] }); },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Test inferenza provider/i);\n assert.match(stdout.toString(), /OpenRouter \\(deepseek-v4-flash\\)/i);\n assert.match(stdout.toString(), /✅ PASS/i);\n // Verify text-only message (no image)\n assert.ok(capturedBody, \"should have sent a request body\");\n assert.equal(capturedBody.messages[0].content.length, 1, \"should send only text content, no image\");\n assert.equal(capturedBody.messages[0].content[0].type, \"text\");\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart force-recreates the Docker runtime even when the container is already running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"recreated\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\")), true);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "f937a6d2ac930e3c037e6c8bcede2cd7c614fc36": {"t": 1785766744.0295823, "n": 230536, "h": "db0064621f6fb064", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart force-recreates the Docker runtime even when the container is already running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-running-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build --force-recreate llmproxy\")) {\n return { status: 0, stdout: \"recreated\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"up -d --build --force-recreate llmproxy\")), true);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "2d20494615007a629b32713b522aa0ddfc5ef607": {"t": 1785766746.0649047, "n": 228489, "h": "4d10aa799b74013b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=unavailable\\n\\s+coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max\\n\\s+credit=n\\/a\\n\\s+coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro\\n\\s+credit=USD 12\\.34\\n\\s+coding=59\\.4\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code\\n\\s+credit=49\\.59\\n\\s+coding=48\\.8\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3\\n\\s+credit=74\\.75 credits\\n\\s+coding=37\\.3\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus\\n\\s+credit=n\\/a\\n\\s+coding=52\\.1\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:test --all-proxies runs real inference across all provider/proxy pairs with grouped output\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n vision: true,\n }, { name: \"Qwen\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n proxyStore.addProxy(\"http://proxy:two@37.27.55.17:7064/\");\n\n const seenBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n seenBodies.push(body);\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: `req-${body.provider}-${seenBodies.length}`,\n success: true,\n finalProvider: body.provider,\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: body.provider, status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(seenBodies.length, 4);\n seenBodies.forEach((body) => {\n assert.match(body.messages[0].content[0].text, /^Rispondi solo: llmproxy-proxy-test-/);\n assert.equal(body.messages[0].content.length, 1);\n });\n assert.match(stdout.toString(), /Test di inferenza provider × proxy/);\n assert.match(stdout.toString(), /DeepSeek \\(deepseek-v4-flash\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n 37\\.27\\.55\\.17\\n OK deepseek-v4-flash\\n risposta testuale non visibile, ma inferenza completata correttamente\\n\\nQwen \\(qwen3\\.7-plus\\)/);\n assert.doesNotMatch(stdout.toString(), /Visione|visione|immagine/);\n assert.match(stdout.toString(), /Risultati: 4 pass, 0 fail, 0 skip/);\n});\n\ntest(\"provider:test --all-proxies accepts canonical finalProvider for instance-scoped providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-opencode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n }, { name: \"opencode-alessio\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, `${JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-instance\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: body.model,\n finalStatus: 200,\n providerSequence: [{ provider: \"opencode\", status: 200, success: true, effective_model: body.model, actual_model: body.model }],\n })}\\n`, \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n model: body.model,\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio \\(deepseek-v4-flash-free\\)\\n 77\\.42\\.22\\.198\\n OK deepseek-v4-flash-free/i);\n assert.doesNotMatch(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 1 pass, 0 fail, 0 skip/i);\n});\n\ntest(\"provider:test --all-proxies returns a failing exit code when at least one provider-proxy pair fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-all-proxies-exit-code-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const proxyStore = require(\"../lib/proxy-store\").createProxyStore({ filePath: path.join(runtimeRoot, \"proxy-registry.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"sk-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n vision: false,\n }, { name: \"DeepSeek\" });\n\n proxyStore.addProxy(\"http://proxy:one@77.42.22.198:7064/\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\", \"--all-proxies\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n model: \"deepseek-v4-flash\",\n content: [{ type: \"text\", text: \"[llmproxy] provider: hidden | model: hidden\" }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /FAIL risposta vuota/i);\n assert.match(stdout.toString(), /Risultati: 0 pass, 1 fail, 0 skip/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_SHORT_ANSWER\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_SHORT_ANSWER\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /può essere impostata soltanto nello scope 'service'/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /├─ Providers/);\n assert.match(stdout.toString(), /│ 1 qwen\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek\\s+1\\s+60\\s+50\\s+10 │/);\n assert.match(stdout.toString(), /├─ Models/);\n assert.match(stdout.toString(), /│ 1 qwen3\\.7-max\\s+2\\s+145\\s+125\\s+20 │/);\n assert.match(stdout.toString(), /│ 2 deepseek-v4-pro\\s+1\\s+60\\s+50\\s+10 │/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- Release 0\\.2\\.60\\./);\n assert.match(stdout.toString(), /- Embed note di rilascio correnti nel pacchetto installato per compatibilità first-update\\./);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "8ce338404ea288515ab1d96d1486d2790747aca5": {"t": 1785766747.7880194, "n": 222058, "h": "77f8203dac9cc740", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add persists proxy rotation and provider:list renders proxy:rotation\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-proxy-rotation-\"));\n const stdoutAdd = createWritableBuffer();\n const stdoutList = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const addExitCode = await runCli([\n \"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\",\n \"--api-key\", \"sk-zen-test\",\n \"--model\", \"deepseek-v4-flash-free\",\n \"--vision\", \"false\",\n \"--proxy\",\n \"--free-model\",\n ], {\n dataRoot: runtimeRoot,\n stdout: stdoutAdd,\n fetchFn,\n });\n\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: stdoutList,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-alessio\");\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.equal(provider.proxy_rotation, true);\n assert.match(stdoutList.toString(), /proxy=proxy:rotation/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "7386e60d789110e7e7b9466a61b28df166e94e15": {"t": 1785766749.6532273, "n": 220735, "h": "ffc67dd5cec8ec18", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:reorder reports 'not configured' when LLMPROXY_REORDERING is unset\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-off-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /non configurata/);\n});\n\ntest(\"provider:reorder ranks by price and persists the new order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-reorder-price-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"paid\", {\n access_token: \"token-paid\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"deepseek\", auth_type: \"api_key\", default_model: \"deepseek-chat\", free_model: false,\n }, { name: \"Paid\" });\n tokenStore.saveProvider(\"free\", {\n access_token: \"token-free\", token_type: \"api_key\", scope: \"api_key\",\n provider: \"openrouter\", auth_type: \"api_key\", default_model: \"some-free-model\", free_model: true,\n }, { name: \"Free\" });\n\n const fetchFn = async () => ({ ok: false }); // pricing lookup fails for the paid provider -> treated as worst, free wins\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:reorder\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: { LLMPROXY_REORDERING: \"price\" },\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"free\", \"paid\"]);\n assert.match(stdout.toString(), /Criteri: price/);\n assert.match(stdout.toString(), /Nuovo ordine provider: free, paid/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "510fbe499bc9c6040e2f6fe6d646562e0a51b227": {"t": 1785766751.6953924, "n": 218514, "h": "e868b4adc012c24b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | oggi: qwen3.7-max 256 (in 20, out 236) | settimana: qwen3.7-max 256 (in 20, out 236) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | oggi: kimi-k2.7-code 357 (in 101, out 256) | settimana: kimi-k2.7-code 357 (in 101, out 256) | `llmproxy stats` per la tabella completa\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "a1ab3a2f48b1c04f8e8c38b9aa22aeb5e333ca29": {"t": 1785766754.1710377, "n": 218364, "h": "14a920bc465c4a6b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript, buildProxyAgentUrl } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"buildProxyAgentUrl preserves an explicit host:port proxy target\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl defaults to port 7064 when only the host is provided\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118\", \"\"), \"http://135.181.79.118:7064\");\n});\n\ntest(\"buildProxyAgentUrl embeds proxy auth without duplicating the port\", () => {\n assert.equal(buildProxyAgentUrl(\"135.181.79.118:7064\", \"secret\"), \"http://proxy:secret@135.181.79.118:7064\");\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n cwd: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add allows multiple instances of same provider/model with different --name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-duplicate-name-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Primary\", \"--api-key\", \"sk-opencode-primary\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--name\", \"Opencode Backup\", \"--api-key\", \"sk-opencode-backup\", \"--model\", \"opencode-large\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const providers = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(providers.length, 2);\n assert.equal(providers[0].access_token, \"sk-opencode-primary\");\n assert.equal(providers[1].access_token, \"sk-opencode-backup\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: path.join(__dirname, \"..\"),\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "ab7da61d555e3358c029ac7743fa611aa21dada5": {"t": 1785766755.8938143, "n": 216453, "h": "3716907fbe4b8a25", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add treats custom opencode instance ids with api-key as api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode-bacin\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.provider, \"opencode\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.vision, false);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.doesNotMatch(stdout.toString(), /Apri https:\\/\\/github\\.com\\/login\\/device/);\n});\n\ntest(\"provider:add keeps separate custom instance ids even when provider and model are identical\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-duplicate-instance-\"));\n const stdout1 = createWritableBuffer();\n const stdout2 = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-bacin\", \"--api-key\", \"sk-zen-test-1\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout1,\n fetchFn,\n });\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-alessio\", \"--api-key\", \"sk-zen-test-2\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n const ids = allProviders.map((provider) => provider.id).sort();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.deepEqual(ids, [\"opencode-alessio\", \"opencode-bacin\"]);\n assert.match(stdout1.toString(), /Provider configurato con API key: opencode-bacin/);\n assert.match(stdout2.toString(), /Provider configurato con API key: opencode-alessio/);\n});\n\ntest(\"provider:list shows vision and free flags for custom api-key provider instances\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-opencode-instance-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"opencode-alessio\", {\n access_token: \"sk-zen-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n vision: false,\n free_model: true,\n }, { name: \"opencode-alessio\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; }, async text() { return \"not found\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode-alessio/);\n assert.match(stdout.toString(), /vision=false free=true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "4ebdcded9879dbb7fb80668183e2e2d313d11e0b": {"t": 1785766757.9720793, "n": 212650, "h": "ee6215bd62a3422a", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"update exits early when the installed version is newer than the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-ahead-of-remote-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return { version: \"0.0.1\" };\n },\n }),\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "9d7d48cafc97dd3ce905f3f7119d7b149401cb1c": {"t": 1785766759.7420805, "n": 211658, "h": "563feb70880d7c2a", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /arresta prima il servizio con privilegi amministrativi/i);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "e16d94b31ee1b37084a8b75f60881bfd553538c7": {"t": 1785766762.296522, "n": 211620, "h": "79812e07d30fa26d", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"config commands bypass the REST wrapper even when the local service is reachable\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-rest-bypass-\"));\n const runtimeRoot = path.join(root, \"runtime\");\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n const requests = [];\n\n fs.mkdirSync(runtimeRoot, { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n const setStdout = createWritableBuffer();\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: setStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(setStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalSettings.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-demo\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test accepts a project-scoped LLMPROXY_LLM_STATS_API_KEY even when Claude proxy markers are absent\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-project-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-project-only\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({ env: {} }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n stderr,\n fetchFn,\n tokenStore,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /llmp service:stop/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "f0b4cd4e920a780873a532fb39d8674c573382e2": {"t": 1785766764.1512241, "n": 207206, "h": "f206ff4f4d16fc1b", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"version bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(stdout.toString().trim(), require(\"../package.json\").version);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /llmp service:stop/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "89b3696a9909288c0a940b45de6c233f2c265ef9": {"t": 1785766766.3803446, "n": 206435, "h": "e7adf6d86a1d343e", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update on Windows stops early with admin guidance when the llmproxy service is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-service-admin-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n if (command === \"sc.exe\" && args[0] === \"query\" && args[1] === \"llmproxy\") {\n return { status: 0, stdout: \"STATE : 4 RUNNING\\n\", stderr: \"\" };\n }\n if (command === \"powershell.exe\" && args.includes(\"WindowsBuiltInRole\")) {\n return { status: 0, stdout: \"0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /PowerShell.*Amministratore/i);\n assert.match(stderr.toString(), /llmp up/);\n assert.match(stderr.toString(), /llmp service:stop/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "5bf933a06861f75141490bd3e0b2ac261fe871ab": {"t": 1785766768.229102, "n": 204890, "h": "00cab15b2141085c", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Quote-CmdArgument\\(\\[string\\]\\$Value\\)/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"cmd\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"\\/d\", \"\\/s\", \"\\/c\", \\$cmdLine\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"powershell\\.exe\"/);\n assert.match(windowsScriptText, /\\$resolvedArguments = @\\(\"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\", \"-File\", \\$FilePath\\) \\+ @\\(\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /\\$resolvedFilePath = \"node\\.exe\"/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "09c4ac8b1a2ea39e8f9f3a43993655b0c7556b19": {"t": 1785766770.0007455, "n": 204338, "h": "2c55f70157fcb3b5", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /& llmproxy service:stop 2>\\$null \\| Out-Null/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "ac3e7d8a90768f8f997e2781d41e4bf26454c690": {"t": 1785766772.2934947, "n": 204183, "h": "fbe5af30613fc8de", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Invoke-QuietNative\\(\\[string\\]\\$FilePath, \\[string\\[\\]\\]\\$ArgumentList\\)/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"git\" @\\(\"clone\", \"--depth=1\"/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"npm\" @\\(\"install\", \"-g\", \"--force\", \"\\$packageFile\"\\)/);\n assert.match(windowsScriptText, /Invoke-QuietNative \"\\$newBin\" @\\(\"config:migrate\"\\)/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "40edbc06c0ce69c73eb7c5b35ac42e1f22670a35": {"t": 1785766774.086019, "n": 204011, "h": "82df7a9d79fdac30", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /\\$PSNativeCommandUseErrorActionPreference = \\$false/);\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /git clone --depth=1 .* \\*> \\$null/);\n assert.match(windowsScriptText, /npm install -g --force \"\\$packageFile\" \\*> \\$null/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate \\*> \\$null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "efd146aede448c14639dd88f79075e5408a7cfa6": {"t": 1785766776.1224527, "n": 203771, "h": "d04612543f6c7b4c", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /\\$tmpdir = Join-Path/);\n assert.match(script, /npm pack \\$packageRoot --pack-destination \\$tmpdir/);\n assert.match(script, /\\$packageFile = Get-ChildItem \\(Join-Path \\$tmpdir '\\*\\.tgz'\\)/);\n assert.match(script, /npm install -g \\$packageFile 2>&1/);\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "be0c1abf69e997e7295625a5821d2955f29d6af5": {"t": 1785766778.1656847, "n": 203494, "h": "0106e662cdfd282c", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd --version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"cmd.exe\" && args[3] === \"npm.cmd prefix -g\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command, args]) => command === \"cmd.exe\" && args[3] === \"npm.cmd --version\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "3cdba1d55b0436b454a881b2cae7104a4152592a": {"t": 1785766780.0739765, "n": 203575, "h": "361e157410e7efa0", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if ((command === \"npm\" || command === \"npm.cmd\") && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update on Windows accepts npm.cmd during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-win-npmcmd-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\") return { status: 1, stdout: \"\", stderr: \"'npm' is not recognized\" };\n if (command === \"npm.cmd\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm.cmd\" && args[0] === \"prefix\") return { status: 0, stdout: \"C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\n\", stderr: \"\" };\n return { status: 0, stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.11\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"npm.cmd\"), true);\n assert.equal(executed.some(([command]) => command === \"powershell.exe\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.11/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "4e017e26b1f6f57f5be1ee874d51fb259785f315": {"t": 1785766782.1617584, "n": 201672, "h": "50dbce007cdc3ce7", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-en stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-sudo-ticket-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"native\" },\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"systemctl\" && args[0] === \"--version\") return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n if (command === \"bash\") return { status: 0, stdout: \"\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /rerun the command with `sudo`/i);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update stops early when the global npm prefix is protected and sudo is not pre-authorized\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-sudo-ticket-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-protected-prefix-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.chmodSync(npmPrefix, 0o555);\n\n try {\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"--version\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n if (command === \"sudo\" && args[0] === \"-n\") return { status: 1, stdout: \"\", stderr: \"sudo: a password is required\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /sudo -v/);\n assert.match(stderr.toString(), /sudo llmproxy update/);\n } finally {\n fs.chmodSync(npmPrefix, 0o755);\n }\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "b09602ca375ce92300afc4c81afa219213fd9c60": {"t": 1785766783.957811, "n": 197951, "h": "5f2d80b8916fed86", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set/get/unset manages global Claude variables with --scope global\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(homeDir, { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: getStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n\n const listStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: listStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(listStdout.toString(), /Global Claude configuration:/);\n assert.match(listStdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.doesNotMatch(listStdout.toString(), /Project configuration:/);\n assert.doesNotMatch(listStdout.toString(), /Service configuration:/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_LLM_STATS_API_KEY\", \"--scope\", \"global\"], {\n cwd: projectRoot,\n stdout: createWritableBuffer(),\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n\n const globalSettings = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_LLM_STATS_API_KEY\" in (globalSettings.env || {}), false);\n});\n\ntest(\"config scope precedence is project over global over service\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-precedence-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(homeDir, { recursive: true });\n\n const projectStdout = createWritableBuffer();\n let exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_SHORT_ANSWER\", \"1\",\n \"--scope\", \"global\",\n \"--service\",\n \"--project\",\n ], {\n cwd: projectRoot,\n stdout: projectStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(projectStdout.toString(), /Configurazione aggiornata: project\\.LLMPROXY_SHORT_ANSWER=1/);\n\n const projectSettings = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(projectSettings.env.LLMPROXY_SHORT_ANSWER, \"1\");\n\n const globalStdout = createWritableBuffer();\n exitCode = await runCli([\n \"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_LLM_STATS_API_KEY\", \"sk-global-demo\",\n \"--scope\", \"global\",\n \"--service\",\n ], {\n cwd: projectRoot,\n stdout: globalStdout,\n env: { ...process.env, HOME: homeDir },\n });\n assert.equal(exitCode, 0);\n assert.match(globalStdout.toString(), /Configurazione aggiornata: global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /safe_remove_path\\(\\) \\{/);\n assert.match(scriptText, /purge_installation_targets\\(\\) \\{/);\n assert.match(scriptText, /purge_existing_wrappers\\(\\) \\{/);\n assert.match(scriptText, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"\\nnpm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo npm uninstall -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\nfi/);\n assert.match(scriptText, /pnpm remove -g llmproxy >\\/dev\\/null 2>&1 \\|\\| true\\npurge_installation_targets\\npurge_existing_wrappers/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/usr\\/local\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /collect_install_target \"\\/opt\\/homebrew\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /wrapper_targets=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/opt\\/homebrew\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \"\\/opt\\/homebrew\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(scriptText, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /ensure_runtime_home_permissions\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /touch \"\\$runtime_home\\/logs\\/service\\.out\\.log\" \"\\$runtime_home\\/logs\\/service\\.err\\.log\" \"\\$runtime_home\\/logs\\/metering\\.jsonl\"/);\n assert.match(installScript, /sudo chown -R \"\\$runtime_owner\":\"\\$runtime_owner\" \"\\$runtime_home\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(installScript, /ensure_runtime_home_permissions \"\\$LLMPROXY_HOME\" \"\\$runtime_owner\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "954604cc2c84de208568b4edee245da96e7250c0": {"t": 1785766785.7896273, "n": 192244, "h": "d9cd08679691b687", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list inherits the global Claude defaults when the project has no local override\", async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-global-fallback-\"));\n const homeDir = path.join(root, \"home\");\n const projectRoot = path.join(root, \"workspace\");\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(projectRoot, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n LLMPROXY_LLM_STATS_API_KEY: \"sk-global-demo\",\n LLMPROXY_SHORT_ANSWER: \"1\",\n },\n }, null, 2));\n\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: projectRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Global Claude configuration:/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_LLM_STATS_API_KEY=sk-global-demo/);\n assert.match(stdout.toString(), /global\\.LLMPROXY_SHORT_ANSWER=1/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const homeDir = path.join(tempRoot, \"home\");\n fs.mkdirSync(homeDir, { recursive: true });\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n env: { ...process.env, HOME: homeDir },\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "a57eceab443ded1579506dfe229caec5fe600ed1": {"t": 1785766787.5675063, "n": 190680, "h": "83ebf87dd7fb052a", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_short_bin=\\$\\(command -v llmp 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_short_bin_path=\"\\$original_npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /global_short_bin_path=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /ensure_global_short_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$current_short_bin\" \"\\$global_bin_path\" \"\\$global_short_bin_path\" \"\\$original_global_bin_path\" \"\\$original_global_short_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \"\\/usr\\/bin\\/llmp\" \"\\/usr\\/local\\/bin\\/llmp\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_short_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_short_bin\" != \"\\$global_short_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_short_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" > \"\\$current_short_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_short_wrapper_payload\" \\| sudo tee \"\\$current_short_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"persistent install script also cleans and preserves the llmp alias\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /for candidate in \\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\); do/);\n assert.match(script, /existing_bins=\\$\\( \\(which -a llmproxy 2>\\/dev\\/null; which -a llmp 2>\\/dev\\/null\\) \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(script, /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(script, /global_short_bin=\"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(script, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$global_short_bin\" \\]; then/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(bashCall[1][1], /rm -f \"\\$npm_prefix\\/bin\\/llmp\"/);\n assert.match(bashCall[1][1], /rm -f \"\\$pnpm_home\\/bin\\/llmp\"/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "ed3f9978fc0be4654d5e99b694ebb7fdac1360c4": {"t": 1785766789.7208056, "n": 187886, "h": "2fed38163a276585", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n LLMPROXY_LLM_STATS_API_KEY: \"sk-test\",\n },\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test fails when LLMPROXY_LLM_STATS_API_KEY is missing from both project and global Claude settings\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-missing-stats-key-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", { api_key: \"nvapi-test\", provider: \"nvidia\", default_model: \"minimaxai/minimax-m3\", vision: true }, { name: \"NVIDIA MiniMax M3\" });\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n },\n model: \"llmProxy\",\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n env: {},\n }, null, 2));\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir, LLMPROXY_RUNTIME_PROFILE: \"production\" },\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(requests.length, 1);\n assert.equal(requests[0].url, \"http://127.0.0.1:7045/v1/llm/health\");\n assert.match(stdout.toString(), /Proxy stato: OK/i);\n assert.match(stderr.toString(), /LLMPROXY_LLM_STATS_API_KEY is mandatory/i);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "522fffe02eb431af60caa0f297c244cf855ea29f": {"t": 1785766792.2651405, "n": 185528, "h": "7ab2e5ed1297fbd5", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update keeps going when the remote version probe times out\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-version-timeout-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n remoteVersionTimeoutMs: 10,\n fetchFn: async () => new Promise(() => {}),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.07\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.07:\\n- release 0.3.07\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(executed.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.07/);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "33b3ffbc0dcfeed847c28b8457a54ff7d653bb32": {"t": 1785766794.1026597, "n": 184196, "h": "e6d5f5ecac3b6fe5", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.06\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.06:\\n- release 0.3.06\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.06/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const homeDir = path.join(runtimeRoot, \"home\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-legacy\",\n LLMPROXY_SMART_PREFERENCE: \"balanced\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, global, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-legacy\");\n assert.equal(\"LLMPROXY_SMART_PREFERENCE\" in globalPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"config:migrate rewrites the global Claude settings even when cwd has no local .claude folder\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-global-\"));\n const homeDir = path.join(runtimeRoot, \"home\");\n const cwd = path.join(runtimeRoot, \"plain-dir\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(homeDir, \".claude\"), { recursive: true });\n fs.mkdirSync(cwd, { recursive: true });\n fs.writeFileSync(path.join(homeDir, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n LLM_STATS_API_KEY: \"sk-global-only\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd,\n dataRoot: runtimeRoot,\n env: { ...process.env, HOME: homeDir },\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: global/);\n\n const globalPayload = JSON.parse(fs.readFileSync(path.join(homeDir, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(globalPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-global-only\");\n assert.equal(\"LLM_STATS_API_KEY\" in globalPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in globalPayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "fcbead336ab2d22ba7a03155786c6424bd5d9489": {"t": 1785766797.3206463, "n": 182237, "h": "218d60bf8843eec0", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"update bypasses the REST wrapper even when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rest-bypass-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n throw new Error(`unexpected REST call: ${url}`);\n },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, args]);\n if (command === \"git\") return { status: 0, stdout: \"git version 2.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: \"/tmp/npm-prefix\\n\", stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return {\n status: 0,\n stdout: \"changed 1 package\\n__LLMPROXY_VERSION__=0.3.05\\n__LLMPROXY_RELEASE_NOTES_START__\\nChangelog 0.3.05:\\n- release 0.3.05\\n__LLMPROXY_RELEASE_NOTES_END__\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, []);\n assert.equal(commandCalls.some(([command]) => command === \"bash\"), true);\n assert.match(stdout.toString(), /Versione corrente: 0\\.3\\.05/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}, "73acf32090c8718eb5806e8228402a0f0511427a": {"t": 1785766802.1227012, "n": 180553, "h": "dce8adf16ea522d8", "c": "const test = require(\"node:test\");\nconst assert = require(\"node:assert/strict\");\nconst fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nconst { runCli, resolveServiceEnvironment, resolveServiceEntryFile, resolveCliServiceManagerOptions, runSelfUpdate, runSelfUpdateWindows, buildPersistentInstallScript } = require(\"../lib/cli\");\nconst { deriveUserScopedPort } = require(\"../lib/runtime-env\");\n\nfunction createWritableBuffer() {\n let output = \"\";\n return {\n write(chunk) {\n output += String(chunk);\n },\n toString() {\n return output;\n },\n };\n}\n\ntest(\"resolveServiceEnvironment aligns service ports with the CLI runtime env\", () => {\n const serviceEnv = resolveServiceEnvironment({\n env: {\n PORT: \"5045\",\n HOST: \"127.0.0.1\",\n NODE_ENV: \"development\",\n LLMPROXY_ENV: \"development\",\n LLMPROXY_MODE: \"standalone\",\n LLMPROXY_MONGODB_CONNECTION_STRING: \"mongodb://user:pass@localhost:27017/llmproxy\",\n DBLAYER_URL: \"http://localhost:5001\",\n EVENTBUS_URL: \"http://localhost:5048\",\n LLMPROXY_LOG_RETENTION_DAYS: \"7\",\n },\n paths: {\n dataRoot: \"/tmp/llmproxy-runtime\",\n packageRoot: \"/tmp/llmproxy-package\",\n },\n dockerComposeFile: \"/tmp/llmproxy-package/docker-compose.production.yml\",\n });\n\n assert.equal(serviceEnv.PORT, \"5045\");\n assert.equal(serviceEnv.HOST, \"127.0.0.1\");\n assert.equal(serviceEnv.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(serviceEnv.NODE_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_ENV, \"development\");\n assert.equal(serviceEnv.LLMPROXY_RUNTIME_PROFILE, \"development\");\n assert.equal(serviceEnv.LLMPROXY_MODE, \"standalone\");\n assert.equal(serviceEnv.LLMPROXY_SERVICE_RUNTIME, \"native\");\n assert.equal(serviceEnv.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://user:pass@localhost:27017/llmproxy\");\n assert.equal(serviceEnv.DBLAYER_URL, \"http://localhost:5001\");\n assert.equal(serviceEnv.EVENTBUS_URL, \"http://localhost:5048\");\n assert.equal(serviceEnv.LLMPROXY_LOG_RETENTION_DAYS, \"7\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_SERVICE, \"llmproxy\");\n assert.equal(serviceEnv.LLMPROXY_DOCKER_POLL_MS, \"30000\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint by default even in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses native server entrypoint on Linux in production\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"linux\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveServiceEntryFile uses Docker entrypoint only when explicitly set\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n targetPlatform: \"darwin\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/lib/service/docker-launchd-entry.js\");\n});\n\ntest(\"resolveServiceEntryFile uses the native server entrypoint in local development\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"development\" },\n packageRoot: \"/tmp/llmproxy-package\",\n });\n\n assert.equal(entryFile, \"/tmp/llmproxy-package/server.js\");\n});\n\ntest(\"resolveServiceEntryFile allows forcing the native server runtime\", () => {\n const entryFile = resolveServiceEntryFile({\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"node\" },\n packageRoot: \"/tmp/node_modules/llmproxy\",\n });\n\n assert.equal(entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint for installed production runtime\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n launchAgentFile: \"/Users/alessiobacin/Library/LaunchAgents/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"darwin\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n});\n\ntest(\"resolveCliServiceManagerOptions uses native server entrypoint on Linux in production\", () => {\n const options = resolveCliServiceManagerOptions({\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n },\n paths: {\n packageRoot: \"/tmp/node_modules/llmproxy\",\n dataRoot: \"/home/aqdas/.local/share/llmProxy\",\n launchAgentFile: \"/tmp/com.llmproxy.service.plist\",\n systemdUnitFile: \"/tmp/llmproxy.service\",\n stdoutLogFile: \"/tmp/service.out.log\",\n stderrLogFile: \"/tmp/service.err.log\",\n },\n targetPlatform: \"linux\",\n });\n\n assert.equal(options.entryFile, \"/tmp/node_modules/llmproxy/server.js\");\n assert.equal(options.environment.PORT, \"7045\");\n assert.equal(options.environment.HOST, \"127.0.0.1\");\n assert.equal(options.environment.LLMPROXY_GLOBAL_SERVICE, \"1\");\n assert.equal(\"LLMPROXY_SHARED_PROVIDER_REGISTRY\" in options.environment, false);\n});\n\ntest(\"claude:setup creates .claude/settings.json for the current project\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.equal(settings.env.API_TIMEOUT_MS, \"3000000\");\n assert.equal(settings.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, \"1\");\n assert.equal(settings.env.LLMPROXY_LLM_STATS_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_API_KEY, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_FROM_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_EMAIL, \"\");\n assert.equal(settings.env.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE, \"service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update\");\n assert.equal(settings.env.LLMPROXY_AUTO_ESCALATE, \"1\");\n assert.equal(settings.env.LLMPROXY_INFERENCE_INFO_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_METERING_INLINE, \"0\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER, \"power\");\n assert.equal(settings.env.LLMPROXY_PROVIDER_CREDIT_INLINE, \"1\");\n assert.equal(settings.env.LLMPROXY_SHORT_ANSWER, \"0\");\n assert.match(stdout.toString(), /settings\\.json/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup merges env settings without overwriting unrelated project settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-merge-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n EXISTING_FLAG: \"keep-me\",\n ANTHROPIC_BASE_URL: \"http://old-host:9999\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n PORT: \"4242\",\n HOST: \"0.0.0.0\",\n },\n stdout,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.env.EXISTING_FLAG, \"keep-me\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://0.0.0.0:4242\");\n assert.equal(\"ANTHROPIC_AUTH_TOKEN\" in settings.env, false);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/0\\.0\\.0\\.0:4242/);\n assert.match(stdout.toString(), /Supporto globale Claude sincronizzato/);\n});\n\ntest(\"claude:setup loads HOST and PORT from the llmproxy package .env file\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-runtime-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-dotenv-package-\"));\n const stdout = createWritableBuffer();\n\n fs.writeFileSync(path.join(packageRoot, \".env\"), \"PORT=5045\\nHOST=127.0.0.1\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:5045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:5045/);\n});\n\ntest(\"claude:setup uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-project-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-claude-runtime-\"));\n const serviceHome = \"/Users/alessiobacin/Library/Application Support/llmProxy\";\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: serviceHome,\n HOST: \"127.0.0.1\",\n },\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"provider:add performs a dedicated Copilot login and provider:list shows fallback order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-\"));\n const stdout = createWritableBuffer();\n const fetchPayloads = [\n {\n verification_uri: \"https://github.com/login/device\",\n user_code: \"ABCD-EFGH\",\n device_code: \"device-code-1\",\n interval: 0,\n },\n {\n access_token: \"backup-token\",\n token_type: \"bearer\",\n scope: \"read:user\",\n },\n ];\n\n const fetchFn = async () => ({\n ok: true,\n async json() {\n return fetchPayloads.shift();\n },\n });\n\n const addExitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"backup\", \"--name\", \"Backup Copilot\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n sleep: async () => {},\n });\n\n const listStdout = createWritableBuffer();\n const listExitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout: listStdout,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(addExitCode, 0);\n assert.equal(listExitCode, 0);\n assert.match(stdout.toString(), /Login completato/);\n assert.match(listStdout.toString(), /1\\. backup \\(Backup Copilot\\)/);\n});\n\ntest(\"provider:list shows the effective project fallback chain when Claude settings override the model routing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-project-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n scope: \"read:user\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Default Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:5045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4,kimi:kimi-k2.5\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Provider effettivi per il progetto/);\n assert.match(stdout.toString(), /1\\. default \\(Default Copilot\\)\\n\\s+model=gpt-5\\.4 coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.5 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-max coding=n\\/a/);\n});\n\ntest(\"provider:list keeps provider default models when the project sets a global override model\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-\"));\n const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-global-model-workspace-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"Command Code\" });\n\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn: async () => ({ ok: false, status: 404, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=n\\/a/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=n\\/a/);\n assert.match(stdout.toString(), /3\\. commandcode \\(Command Code\\)\\n\\s+model=Qwen\\/Qwen3\\.7-Max coding=n\\/a/);\n assert.doesNotMatch(stdout.toString(), /model=gpt-5\\.4/);\n});\n\ntest(\"provider:list shows residual credit plus current and best provider pricing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-credit-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n\n const fetchFn = async (url) => {\n const target = String(url);\n if (target === \"https://api.deepseek.com/user/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n is_available: true,\n balance_infos: [{ currency: \"USD\", total_balance: \"12.34\" }],\n };\n },\n };\n }\n if (target === \"https://api.moonshot.ai/v1/users/me/balance\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n status: true,\n data: { available_balance: 49.58894 },\n };\n },\n };\n }\n if (target === \"https://openrouter.ai/api/v1/credits\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: { total_credits: 100.5, total_usage: 25.75 },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 59.4 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 48.8 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/benchmarks\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 37.3 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/benchmarks\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n sources: [{ scores: [{ metric: \"coding_index\", value: 52.1 }] }],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/deepseek-v4-pro/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.305,\n breakdown: [\n { dimension: \"input\", unit_price: 0.435 },\n { dimension: \"output\", unit_price: 0.87 },\n ],\n providers: [\n { provider_id: \"deepseek\" },\n { provider_id: \"openrouter\" },\n { provider_id: \"vercel_ai_gateway\" },\n ],\n },\n options: [\n { provider_id: \"deepseek\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.305, breakdown: [{ dimension: \"input\", unit_price: 0.435 }, { dimension: \"output\", unit_price: 0.87 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/kimi-k2.7-code/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 4.24,\n breakdown: [\n { dimension: \"input\", unit_price: 0.74 },\n { dimension: \"output\", unit_price: 3.5 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 4.24, breakdown: [{ dimension: \"input\", unit_price: 0.74 }, { dimension: \"output\", unit_price: 3.5 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/minimax%2Fminimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\"\n || target === \"https://ai.cloudprice.net/api/v1/models/minimax-m3/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.5,\n breakdown: [\n { dimension: \"input\", unit_price: 0.3 },\n { dimension: \"output\", unit_price: 1.2 },\n ],\n providers: [\n { provider_id: \"fireworks_ai\" },\n { provider_id: \"huggingface\" },\n { provider_id: \"minimax\" },\n { provider_id: \"openrouter\" },\n ],\n },\n options: [\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.5, breakdown: [{ dimension: \"input\", unit_price: 0.3 }, { dimension: \"output\", unit_price: 1.2 }] },\n ],\n },\n };\n },\n };\n }\n if (target === \"https://ai.cloudprice.net/api/v1/models/qwen3.7-plus/pricing/calculate?tier=standard&input_tokens=1000000&output_tokens=1000000\") {\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n data: {\n result: {\n total_cost: 1.6,\n breakdown: [\n { dimension: \"input\", unit_price: 0.32 },\n { dimension: \"output\", unit_price: 1.28 },\n ],\n providers: [{ provider_id: \"openrouter\" }],\n },\n options: [\n { provider_id: \"alibaba_qwen\", tier: \"standard\", total_cost: 2.0, breakdown: [{ dimension: \"input\", unit_price: 0.4 }, { dimension: \"output\", unit_price: 1.6 }] },\n { provider_id: \"openrouter\", tier: \"standard\", total_cost: 1.6, breakdown: [{ dimension: \"input\", unit_price: 0.32 }, { dimension: \"output\", unit_price: 1.28 }] },\n ],\n },\n };\n },\n };\n }\n return { ok: false, status: 404, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. deepseek \\(DeepSeek\\)\\n\\s+model=deepseek-v4-pro coding=59\\.4\\n\\s+credit=USD 12\\.34\\n\\s+price=in=USD 0\\.43\\/1M out=USD 0\\.87\\/1M\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /2\\. kimi \\(Kimi\\)\\n\\s+model=kimi-k2\\.7-code coding=48\\.8\\n\\s+credit=49\\.59\\n\\s+price=n\\/a\\n\\s+best=openrouter/);\n assert.match(stdout.toString(), /3\\. openrouter \\(OpenRouter\\)\\n\\s+model=minimax\\/minimax-m3 coding=37\\.3\\n\\s+credit=74\\.75 credits\\n\\s+price=in=USD 0\\.30\\/1M out=USD 1\\.20\\/1M\\n\\s+best=fireworks/);\n assert.match(stdout.toString(), /4\\. qwen \\(Qwen\\)\\n\\s+model=qwen3\\.7-plus coding=52\\.1\\n\\s+credit=n\\/a\\n\\s+price=in=USD 0\\.40\\/1M out=USD 1\\.60\\/1M\\n\\s+best=openrouter/);\n});\n\ntest(\"provider:available shows supported providers with aliases and auth type\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-available-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:available\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. copilot \\(GitHub Copilot\\) auth=oauth/);\n assert.match(stdout.toString(), /2\\. openrouter \\(OpenRouter\\) auth=api_key/);\n assert.match(stdout.toString(), /3\\. zai \\(Z\\.AI\\) auth=api_key aliases=z\\.ai/);\n assert.match(stdout.toString(), /qwen \\(Qwen \\(DashScope\\)\\) auth=api_key/);\n assert.match(stdout.toString(), /opencode \\(OpenCode Zen\\) auth=api_key aliases=zen/);\n assert.match(stdout.toString(), /opencode-go \\(OpenCode Go\\) auth=api_key aliases=go, opencodego/);\n assert.match(stdout.toString(), /nvidia \\(NVIDIA\\) auth=api_key/);\n});\n\ntest(\"provider:add supports api-key providers like openrouter\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-apikey-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--name\", \"OpenRouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"openai/gpt-4o\", \"--vision\", \"true\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"openrouter\");\n\n assert.equal(exitCode, 0);\n assert.ok(provider);\n assert.equal(provider.access_token, \"sk-or-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"openrouter\");\n assert.equal(provider.default_model, \"openai/gpt-4o\");\n assert.equal(provider.free_model, false);\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:add supports api-key providers like nvidia\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-nvidia-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return { ok: true, status: 200, async json() { return {}; } };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"nvidia\", \"--name\", \"NVIDIA\", \"--api-key\", \"nvapi-test\", \"--model\", \"z-ai/glm-5.2\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"nvidia\");\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestUrls, [\"https://integrate.api.nvidia.com/v1/chat/completions\"]);\n assert.ok(provider);\n assert.equal(provider.access_token, \"nvapi-test\");\n assert.equal(provider.auth_type, \"api_key\");\n assert.equal(provider.provider, \"nvidia\");\n assert.equal(provider.default_model, \"z-ai/glm-5.2\");\n assert.match(stdout.toString(), /Provider configurato con API key/);\n});\n\ntest(\"provider:list loads legacy nvidia api-key entries saved without access_token\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-list-nvidia-legacy-\"));\n const stdout = createWritableBuffer();\n const tokenFile = path.join(runtimeRoot, \"copilot-token.json\");\n\n fs.writeFileSync(tokenFile, JSON.stringify({\n version: 2,\n providers: [\n {\n id: \"nvidia\",\n name: \"NVIDIA\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n api_key: \"nvapi-legacy\",\n default_model: \"z-ai/glm-5.2\",\n },\n ],\n order: [\"nvidia\"],\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return {}; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /nvidia/i);\n assert.match(stdout.toString(), /1\\. nvidia \\(NVIDIA\\)/);\n assert.match(stdout.toString(), /model=z-ai\\/glm-5\\.2/);\n});\n\ntest(\"provider:add supports api-key providers like qwen\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"qwen\");\n assert.equal(saved.default_model, \"qwen3.7-max\");\n assert.equal(saved.endpoint_variant, \"dashscope\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(requestBodies[0].model, \"qwen3.7-max\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: false, free: false, plan: payg\\)/);\n});\n\ntest(\"provider:add supports qwen subscription plan explicitly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-subscription-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--name\", \"Qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"true\", \"--plan\", \"subscription\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.equal(saved.vision, true);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.match(stdout.toString(), /Provider configurato con API key: qwen \\(default model: qwen3\\.7-max, vision: true, free: false, plan: subscription\\)/);\n});\n\ntest(\"provider:add supports anthropic-style API-key providers like opencode-go\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-opencode-go-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const requestBodies = [];\n const requestHeaders = [];\n const fetchFn = async (url, options = {}) => {\n requestUrls.push(url);\n requestBodies.push(JSON.parse(String(options.body || \"{}\")));\n requestHeaders.push(options.headers || {});\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"msg_opencode_go\" };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode-go\", \"--name\", \"OpenCode Go\", \"--api-key\", \"sk-opencode-go-test\", \"--model\", \"minimax-m3\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const saved = tokenStore.getProvider(\"opencode-go\");\n assert.equal(exitCode, 0);\n assert.equal(saved.provider, \"opencode-go\");\n assert.equal(saved.default_model, \"minimax-m3\");\n assert.equal(saved.vision, false);\n assert.equal(requestUrls[0], \"https://opencode.ai/zen/go/v1/messages\");\n assert.equal(requestBodies[0].model, \"minimax-m3\");\n assert.equal(requestHeaders[0][\"x-api-key\"], \"sk-opencode-go-test\");\n assert.match(stdout.toString(), /Provider configurato con API key: opencode-go \\(default model: minimax-m3, vision: false, free: false\\)/);\n});\n\ntest(\"provider:key routes qwen token-plan keys to the token-plan endpoint\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-qwen-token-plan-\"));\n const stdout = createWritableBuffer();\n const requestUrls = [];\n const fetchFn = async (url) => {\n requestUrls.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return { id: \"ok\" };\n },\n };\n };\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"sk-old-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n }, { name: \"Qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"qwen\", \"--api-key\", \"sk-sp-token-plan\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const saved = tokenStore.getProvider(\"qwen\");\n assert.equal(exitCode, 0);\n assert.equal(requestUrls[0], \"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions\");\n assert.equal(saved.access_token, \"sk-sp-token-plan\");\n assert.equal(saved.endpoint_variant, \"token_plan\");\n assert.match(stdout.toString(), /API key aggiornata per provider qwen \\(default model: qwen3\\.7-max(?:, free: false)?, plan: subscription\\)/);\n});\n\ntest(\"provider:add stores free_model when requested\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-free-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"opencode\", \"--api-key\", \"sk-zen-test\", \"--model\", \"deepseek-v4-flash-free\", \"--vision\", \"false\", \"--free-model\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const provider = tokenStore.getProvider(\"opencode\");\n\n assert.equal(exitCode, 0);\n assert.equal(provider.free_model, true);\n assert.match(stdout.toString(), /free: true/);\n});\n\ntest(\"provider:add rejects invalid qwen plan values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-qwen-invalid-plan-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"false\", \"--plan\", \"enterprise\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--plan subscription oppure --plan payg/);\n});\n\ntest(\"provider:add requires --vision flag for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-missing-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision <true\\|false>/);\n});\n\ntest(\"provider:add rejects invalid --vision values\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-add-bad-vision-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"qwen\", \"--api-key\", \"sk-qwen-test\", \"--model\", \"qwen3.7-max\", \"--vision\", \"maybe\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /--vision deve essere 'true' oppure 'false'/);\n});\n\ntest(\"provider:add rejects api-key providers when the default model probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-probe-fails-\"));\n const stderr = createWritableBuffer();\n const fetchFn = async () => ({\n ok: false,\n status: 400,\n async text() {\n return \"model_not_supported\";\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\", \"--model\", \"bad-model\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stderr,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 1);\n assert.equal(tokenStore.getProvider(\"openrouter\"), null);\n assert.match(stderr.toString(), /Test provider fallito/);\n});\n\ntest(\"provider:test treats NVIDIA HTTP 429 as a reachable provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-test-nvidia-429-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia-glm-5.2\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"z-ai/glm-5.2\",\n vision: false,\n }, { name: \"NVIDIA\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: false, status: 429, async text() { return \"rate limit\"; } }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /NVIDIA \\(z-ai\\/glm-5\\.2\\)/i);\n assert.match(stdout.toString(), /PASS - Visione correttamente disabilitata \\(errore HTTP 429\\)/i);\n});\n\ntest(\"provider:add requires a default model for api-key providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-model-required-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"openrouter\", \"--api-key\", \"sk-or-test\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /richiede --model/);\n});\n\ntest(\"provider:key updates api key for an existing provider\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-key-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"sk-old\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"openai/gpt-4o\",\n }, { name: \"OpenRouter\" });\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return {}; } });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:key\", \"openrouter\", \"--api-key\", \"sk-new\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"openrouter\").access_token, \"sk-new\");\n assert.match(stdout.toString(), /API key aggiornata/);\n});\n\ntest(\"provider:order moves providers to the requested fallback position\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-order-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:order\", \"backup\", \"1\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.deepEqual(reloaded.listProviders().map((provider) => provider.id), [\"backup\", \"primary\"]);\n assert.match(stdout.toString(), /backup/);\n});\n\ntest(\"provider:rename updates the provider display name\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-rename-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Old Name\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:rename\", \"backup\", \"New Backup Name\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const reloaded = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n assert.equal(exitCode, 0);\n assert.equal(reloaded.getProvider(\"backup\").name, \"New Backup Name\");\n assert.match(stdout.toString(), /New Backup Name/);\n});\n\ntest(\"provider:status shows ordered providers and identifies the active one\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-provider-status-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-max\",\n endpoint_variant: \"token_plan\",\n }, { name: \"Qwen\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:status\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Active provider: backup/);\n assert.match(stdout.toString(), /1\\. backup \\(Backup\\) \\[active\\]/);\n assert.match(stdout.toString(), /2\\. primary \\(Primary\\)/);\n assert.match(stdout.toString(), /3\\. qwen \\(Qwen\\).*plan=subscription/);\n});\n\ntest(\"status shows configured fallback provider order\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-extended-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n\n tokenStore.saveProvider(\"primary\", { access_token: \"token-primary\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Primary\" });\n tokenStore.saveProvider(\"backup\", { access_token: \"token-backup\", token_type: \"bearer\", scope: \"read:user\" }, { name: \"Backup\" });\n tokenStore.moveProvider(\"backup\", 1);\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: true, active: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Fallback order: backup, primary/);\n assert.match(stdout.toString(), /Active provider: backup/);\n});\n\ntest(\"logs prints structured request logs when service stdout and stderr are empty\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-logs-\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"service.out.log\"), \"\", \"utf8\");\n fs.writeFileSync(path.join(logsDir, \"service.err.log\"), \"\", \"utf8\");\n fs.writeFileSync(\n path.join(logsDir, \"requests-2026-03-27.jsonl\"),\n `${JSON.stringify({ event: \"request_in\", model: \"glm-5\" })}\\n${JSON.stringify({ event: \"provider_result\", error: \"model_not_supported\" })}\\n`,\n \"utf8\",\n );\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"logs\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /request_in/);\n assert.match(stdout.toString(), /glm-5/);\n assert.match(stdout.toString(), /model_not_supported/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: false,\n stderr: \"bootstrap failed\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /bootstrap failed/);\n});\n\ntest(\"service:start verifies Docker runtime and proxy health on production installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Servizio installato con launchd/);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:start falls back to legacy docker-compose when the plugin is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n const composeSpawnOptions = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args, spawnOptions) {\n commandCalls.push([command, ...args]);\n if ((command === \"docker\" && args[0] === \"compose\") || command === \"docker-compose\") {\n composeSpawnOptions.push(spawnOptions);\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n const joined = args.join(\" \");\n if (command === \"docker-compose\" && joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (command === \"docker-compose\" && joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.equal(commandCalls.some((call) => call[0] === \"docker-compose\" && call[1] === \"version\"), true);\n assert.equal(composeSpawnOptions.some((entry) => entry?.env?.LLMPROXY_HOME === runtimeRoot), true);\n});\n\ntest(\"service:restart on launchd reinstalls the agent directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-fallback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: \"/tmp/service.out.log\",\n stderrPath: \"/tmp/service.err.log\",\n };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on launchd falls back to start when install is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-launchd-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"launchd\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"start\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart on windows reinstalls the service directly\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-windows-direct-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const calls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n serviceManager: {\n kind: \"windows\",\n stop() {\n calls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n install() {\n calls.push(\"install\");\n return {\n ok: true,\n stdout: \"\",\n stderr: \"\",\n stdoutPath: path.join(runtimeRoot, \"service.out.log\"),\n stderrPath: path.join(runtimeRoot, \"service.err.log\"),\n };\n },\n start() {\n calls.push(\"start\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(calls, [\"install\"]);\n assert.match(stdout.toString(), /Servizio riavviato/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:restart recovers the Docker runtime when the managed container is not running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-restart-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:restart\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasRestarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasRestarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Runtime Docker: container ricreato/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime docker removes native artifacts and starts the docker runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-docker-home-\"));\n const launchAgentFile = path.join(homeDir, \"Library\", \"LaunchAgents\", \"com.llmproxy.service.plist\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n fs.mkdirSync(path.dirname(launchAgentFile), { recursive: true });\n fs.writeFileSync(launchAgentFile, \"<plist></plist>\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"docker\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n homeDir,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"native\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n const wasStarted = commandCalls.some((call) => call.join(\" \").includes(\"up -d --build llmproxy\"));\n return { status: 0, stdout: wasStarted ? \"llmproxy\\n\" : \"\", stderr: \"\" };\n }\n if (joined.includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n if (joined.includes(\"up -d --build llmproxy\")) {\n return { status: 0, stdout: \"started\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(fs.existsSync(launchAgentFile), false);\n assert.match(stdout.toString(), /Runtime attivo: docker/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"service:runtime native stops the docker runtime and installs the native service\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-runtime-native-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n let installCalled = false;\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:runtime\", \"launchd\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, async json() { return { ok: true }; } }),\n commandRunner(command, args) {\n commandCalls.push([command, ...args]);\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (args.join(\" \").includes(\"down --remove-orphans\")) {\n return { status: 0, stdout: \"removed\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n install() {\n installCalled = true;\n return { ok: true, stdout: \"\", stderr: \"\", stdoutPath: \"/tmp/service.out.log\", stderrPath: \"/tmp/service.err.log\" };\n },\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n start() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(installCalled, true);\n assert.equal(commandCalls.some((call) => call.join(\" \").includes(\"down --remove-orphans\")), true);\n assert.match(stdout.toString(), /Runtime attivo: native/);\n assert.match(stdout.toString(), /Health check OK/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"status reports Docker runtime activity when the service runtime is docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_DOCKER_COMPOSE_FILE: composeFile,\n LLMPROXY_DOCKER_SERVICE: \"llmproxy\",\n LLMPROXY_SERVICE_RUNTIME: \"docker\",\n },\n stdout,\n stderr,\n commandRunner(command, args) {\n const joined = args.join(\" \");\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (joined.includes(\"ps --status running --services llmproxy\")) {\n return { status: 0, stdout: \"llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n status() {\n return { ok: false, active: false, stdout: \"\", stderr: \"launchd inactive\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: docker/);\n assert.match(stdout.toString(), /Service active: yes/);\n assert.match(stdout.toString(), /llmproxy container active via docker compose/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"windows status reports inactive cleanly when the service is not installed\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-status-windows-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"status\"], {\n dataRoot: runtimeRoot,\n platform: \"win32\",\n stdout,\n stderr,\n serviceManager: {\n kind: \"windows\",\n status() {\n return { ok: true, active: false, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Service manager: windows/);\n assert.match(stdout.toString(), /Service active: no/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"runCli uses the REST wrapper when the local service is reachable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-rest-wrapper-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"provider:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n restFetchFn: async (url) => {\n requests.push(String(url));\n if (String(url).endsWith(\"/health\")) {\n return { ok: true, async json() { return { ok: true }; } };\n }\n return {\n ok: true,\n async json() {\n return {\n success: true,\n exitCode: 0,\n data: {\n output: \"1. rest (REST Provider) model=gpt-5.4\",\n error: \"\",\n },\n };\n },\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requests, [\n \"http://127.0.0.1:5045/health\",\n \"http://127.0.0.1:5045/api/providers\",\n ]);\n assert.match(stdout.toString(), /REST Provider/);\n});\n\ntest(\"models:list prints a numbered list of available models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-list-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. /);\n assert.match(stdout.toString(), /claude-sonnet-4\\.5|claude-opus-4\\.5|gpt-5/);\n});\n\ntest(\"models:list uses the live Copilot model catalog when authenticated\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"models:list\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /1\\. gpt-4\\.1/);\n assert.match(stdout.toString(), /2\\. o3/);\n});\n\ntest(\"test sends a fixed inference prompt to the local proxy and prints the assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-command-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ciao creatore\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.equal(requests.length, 2);\n assert.equal(requests[0].url, \"http://127.0.0.1:5045/v1/llm/health\");\n assert.equal(requests[1].url, \"http://127.0.0.1:5045/v1/messages\");\n assert.equal(requests[1].options.method, \"POST\");\n assert.equal(requests[1].options.headers[\"content-type\"], \"application/json\");\n\n const body = JSON.parse(requests[1].options.body);\n assert.equal(body.stream, false);\n assert.equal(body.max_tokens, 256);\n assert.equal(body.model, \"claude-sonnet-4.5\");\n assert.equal(body.messages[0].role, \"user\");\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-auto\");\n assert.match(stdout.toString(), /auto: ok \\(claude-sonnet-4\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ciao creatore/);\n});\n\ntest(\"test uses a deterministic per-user port when no explicit PORT is configured\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-user-port-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-package-no-env-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/llm/health`);\n assert.equal(requests[1], `http://127.0.0.1:${deriveUserScopedPort(runtimeRoot)}/v1/messages`);\n});\n\ntest(\"test uses the production service port 7045 when the installed CLI profile is active\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-production-port-\"));\n const stdout = createWritableBuffer();\n const requests = [];\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"auto\", { access_token: \"token-auto\", token_type: \"bearer\", scope: \"read:user\", provider: \"copilot\", default_model: \"claude-sonnet-4.5\" }, { name: \"auto\" });\n\n const fetchFn = async (url) => {\n requests.push(url);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"claude-sonnet-4.5\",\n content: [{ type: \"text\", text: \"ok\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests[0], \"http://127.0.0.1:7045/v1/llm/health\");\n assert.equal(requests[1], \"http://127.0.0.1:7045/v1/messages\");\n});\n\ntest(\"development runtime uses the fixed local proxy port 5045\", async () => {\n const leftRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-left-\"));\n const rightRuntimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-port-right-\"));\n\n assert.equal(deriveUserScopedPort(leftRuntimeRoot), \"5045\");\n assert.equal(deriveUserScopedPort(rightRuntimeRoot), \"5045\");\n});\n\ntest(\"test probes only the active provider by default\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n});\n\ntest(\"test prints provider HTTP error details when the quick inference probe fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-http-error-detail-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"nvidia\", {\n access_token: \"nvapi-test\",\n token_type: \"api_key\",\n scope: \"api_key\",\n provider: \"nvidia\",\n auth_type: \"api_key\",\n default_model: \"minimaxai/minimax-m3\",\n vision: true,\n }, { name: \"NVIDIA MiniMax M3\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n return {\n ok: false,\n status: 400,\n async text() {\n return JSON.stringify({ error: { message: \"model requires a different endpoint variant\" } });\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /nvidia: fail HTTP 400: model requires a different endpoint variant \\(minimaxai\\/minimax-m3\\)/);\n});\n\ntest(\"test hides llmproxy metadata lines from the printed assistant reply\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-hide-metadata-\"));\n const stdout = createWritableBuffer();\n const { createTokenStore } = require(\"../lib/token-store\");\n const tokenStore = createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"qwen\", { access_token: \"token-qwen\", token_type: \"api_key\", scope: \"api_key\", provider: \"qwen\", default_model: \"qwen3.7-max\" }, { name: \"qwen\" });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n content: [{\n type: \"text\",\n text: \"llmproxy-test-qwen\\n\\n[llmproxy] provider: qwen | model: qwen3.7-max\\n[llmproxy] tokens: req 256 (in 20, out 236) | provider today 256 week 256 | model today 256 week 256\",\n }],\n };\n },\n }),\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /qwen: ok \\([^)]+\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-qwen/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] provider:/);\n assert.doesNotMatch(stdout.toString(), /\\[llmproxy\\] tokens:/);\n});\n\ntest(\"test retries once after a transient local fetch failure\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-retry-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"default\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n\n let messageAttempts = 0;\n const sleepCalls = [];\n const fetchFn = async (url) => {\n if (String(url).includes(\"/v1/llm/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n messageAttempts += 1;\n if (messageAttempts === 1) throw new Error(\"fetch failed\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n content: [{ type: \"text\", text: \"llmproxy-test-default\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep(ms) {\n sleepCalls.push(ms);\n return Promise.resolve();\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(messageAttempts, 2);\n assert.deepEqual(sleepCalls, [250]);\n assert.match(stdout.toString(), /default: ok \\(gpt-5\\.4\\)/);\n assert.doesNotMatch(stdout.toString(), /llmproxy-test-default/);\n});\n\ntest(\"claude:setup accepts a model index from the numbered model list\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model:/);\n});\n\ntest(\"model:set updates the Claude project model with a raw provider-prefixed value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n permissions: {\n allow: [\"Bash(node:*)\"],\n },\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n KEEP_ME: \"yes\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"deepseek:deepseek-v4-flash\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.deepEqual(settings.permissions, { allow: [\"Bash(node:*)\"] });\n assert.equal(settings.model, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.ANTHROPIC_DEFAULT_MODEL, \"deepseek:deepseek-v4-flash\");\n assert.equal(settings.env.KEEP_ME, \"yes\");\n assert.match(stdout.toString(), /Default model: deepseek:deepseek-v4-flash/);\n});\n\ntest(\"model:set rewrites ANTHROPIC_BASE_URL to the production service port 7045 for installed CLI profile\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-production-\"));\n const claudeDir = path.join(tempRoot, \".claude\");\n const settingsFile = path.join(claudeDir, \"settings.json\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(claudeDir, { recursive: true });\n fs.writeFileSync(settingsFile, JSON.stringify({\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:12345\",\n ANTHROPIC_DEFAULT_MODEL: \"copilot:gpt-5.4\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\", \"gpt-5.4\"], {\n cwd: tempRoot,\n stdout,\n env: {\n LLMPROXY_RUNTIME_PROFILE: \"production\",\n LLMPROXY_HOME: \"/Users/alessiobacin/Library/Application Support/llmProxy\",\n HOST: \"127.0.0.1\",\n },\n });\n\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.env.ANTHROPIC_BASE_URL, \"http://127.0.0.1:7045\");\n assert.match(stdout.toString(), /http:\\/\\/127\\.0\\.0\\.1:7045/);\n});\n\ntest(\"model:set rejects an empty model value\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-set-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"model:set\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Uso: llmproxy model:set <model>/);\n});\n\ntest(\"config:set/get/unset manages project-scoped variables in Claude settings\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-project-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"1\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n stderr,\n });\n assert.equal(exitCode, 0);\n\n const settings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(settings.env.LLMPROXY_PRICE_PERFORMANCE_ROUTING, \"1\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n\n const unsetStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_PRICE_PERFORMANCE_ROUTING\", \"--project\"], {\n cwd: tempRoot,\n stdout: unsetStdout,\n });\n assert.equal(exitCode, 0);\n\n const nextSettings = JSON.parse(fs.readFileSync(path.join(tempRoot, \".claude\", \"settings.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_PRICE_PERFORMANCE_ROUTING\" in nextSettings.env, false);\n});\n\ntest(\"config:list shows effective llmproxy project defaults when values are unset\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-defaults-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\", \"--project\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /project\\.LLMPROXY_AUTO_ESCALATE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_LLM_STATS_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_API_KEY=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_FROM_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_EMAIL=/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SENDGRID_TO_MESSAGE_TYPE=service_unreachable,service_recovered,provider_error,auto_escalation,provider_credit_exhausted,service_update/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_INFERENCE_INFO_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_METERING_INLINE=0/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_ROUTING=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PRICE_PERFORMANCE_TIEBREAKER=power/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_PROVIDER_CREDIT_INLINE=1/);\n assert.match(stdout.toString(), /project\\.LLMPROXY_SHORT_ANSWER=0/);\n});\n\ntest(\"config:list outside a project shows only service configuration\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-only-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:list\"], {\n cwd: tempRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.doesNotMatch(stdout.toString(), /Project configuration:/);\n assert.match(stdout.toString(), /Service configuration:/);\n});\n\ntest(\"config:set/get/unset manages service-scoped variables in the persistent service config\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-service-\"));\n\n let exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async (url) => ({\n ok: String(url).includes(\"7001/health\") || String(url).includes(\"7048/health\"),\n status: 200,\n }),\n });\n assert.equal(exitCode, 0);\n\n const serviceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(serviceConfig.env.LLMPROXY_MODE, \"platform\");\n\n const getStdout = createWritableBuffer();\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:get\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: getStdout,\n });\n assert.equal(exitCode, 0);\n assert.match(getStdout.toString(), /service\\.LLMPROXY_MODE=platform/);\n\n exitCode = await runCli([\"node\", \"llmproxy\", \"config:unset\", \"LLMPROXY_MODE\", \"--service\"], {\n dataRoot: runtimeRoot,\n stdout: createWritableBuffer(),\n });\n assert.equal(exitCode, 0);\n\n const nextServiceConfig = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(\"LLMPROXY_MODE\" in nextServiceConfig.env, false);\n});\n\ntest(\"config:set rejects switching to platform mode when db-layer or event-bus are unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-platform-validation-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--service\"], {\n dataRoot: runtimeRoot,\n stderr,\n env: { LLMPROXY_RUNTIME_PROFILE: \"production\" },\n fetchFn: async () => ({ ok: false, status: 503 }),\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /Non e' possibile passare a LLMPROXY_MODE=platform/);\n assert.match(stderr.toString(), /db-layer/);\n assert.match(stderr.toString(), /event-bus/);\n});\n\ntest(\"config:set rejects scope mismatches\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-scope-mismatch-\"));\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:set\", \"LLMPROXY_MODE\", \"platform\", \"--project\"], {\n dataRoot: runtimeRoot,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stderr.toString(), /appartiene allo scope service/);\n});\n\ntest(\"claude:setup resolves model indexes from the live Copilot catalog\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-live-select-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.save({ access_token: \"token-live\", token_type: \"bearer\", scope: \"read:user\" });\n\n const fetchFn = async () => ({\n ok: true,\n status: 200,\n async json() {\n return {\n data: [\n { id: \"gpt-4.1\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n { id: \"o3\", object: \"model\", model_picker_enabled: true, policy: { state: \"enabled\" } },\n ],\n };\n },\n });\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"2\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n });\n\n const settingsFile = path.join(tempRoot, \".claude\", \"settings.json\");\n const settings = JSON.parse(fs.readFileSync(settingsFile, \"utf8\"));\n\n assert.equal(exitCode, 0);\n assert.equal(settings.model, \"llmProxy\");\n assert.equal(\"ANTHROPIC_DEFAULT_MODEL\" in settings.env, false);\n assert.match(stdout.toString(), /Default model: o3/);\n});\n\ntest(\"install:persistent-it installs the current package globally and starts the persistent macOS service in Italian\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.equal(installCall.spawnOptions.encoding, \"utf8\");\n assert.match(installCall.args[1], /case \"\\$platform\" in/);\n assert.match(installCall.args[1], /darwin\\|linux\\)/);\n assert.match(installCall.args[1], /npm install -g /);\n assert.match(installCall.args[1], /LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /\\/usr\\/local\\/bin\\/llmproxy/);\n});\n\ntest(\"install:persistent-en installs the current package globally and starts the persistent macOS service in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-macos-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install:persistent-it prints linger guidance on Linux\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en prints linger guidance on Linux in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args) {\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Linux note:/);\n assert.match(stdout.toString(), /loginctl enable-linger/);\n});\n\ntest(\"install:persistent-en best-effort enables linger during Linux bootstrap\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-linger-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(installCall.args[1], /linger_user=\\$\\{SUDO_USER:-\\$USER\\}/);\n assert.match(installCall.args[1], /sudo -n loginctl enable-linger \"\\$linger_user\"/);\n});\n\ntest(\"service:start returns an error when the service manager install fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-service-start-fail-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"service:start\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n serviceManager: {\n kind: \"systemd\",\n install() {\n return { ok: false, stderr: \"Failed to connect to bus: No medium found\\n\" };\n },\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Failed to connect to bus: No medium found/);\n});\n\ntest(\"install:persistent-it succeeds on Windows\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.match(stdout.toString(), /Servizio persistente attivato/);\n});\n\ntest(\"install:persistent-en succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-en-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"windows persistent install script resolves the global cmd wrapper explicitly\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"win32\",\n });\n assert.match(script, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(script, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(script, /\\$globalBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n});\n\ntest(\"runSelfUpdateWindows resolves the global cmd wrapper explicitly\", () => {\n const executed = [];\n runSelfUpdateWindows((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n const windowsScriptText = executed[0][1][2];\n assert.match(windowsScriptText, /function Resolve-LlmproxyGlobalBin\\(\\[string\\]\\$Prefix\\)/);\n assert.match(windowsScriptText, /Join-Path \\$Prefix \"llmproxy\\.cmd\"/);\n assert.match(windowsScriptText, /\\$newBin = Resolve-LlmproxyGlobalBin \\$npmPrefix/);\n assert.match(windowsScriptText, /& \"\\$newBin\" config:migrate 2>\\$null \\| Out-Null/);\n});\n\ntest(\"install:persistent-it fails with prerequisite guidance when Docker is missing on Ubuntu\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-fail-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-preflight-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n osReleaseContent: 'ID=ubuntu\\nID_LIKE=debian\\n',\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n if (command === \"docker\" && args[0] === \"--version\") {\n return { status: 1, stdout: \"\", stderr: \"not found\\n\" };\n }\n throw new Error(`Unexpected command: ${command} ${args.join(\" \")}`);\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /I prerequisiti per l'installazione persistente non sono soddisfatti/);\n assert.match(stderr.toString(), /Docker non trovato nel PATH/);\n assert.match(stderr.toString(), /sudo apt update && sudo apt install -y docker\\.io docker-compose-v2/);\n assert.equal(commandCalls.some((entry) => entry.command === \"sh\"), false);\n});\n\ntest(\"install:persistent-it accepts legacy docker-compose during preflight\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-persistent-legacy-compose-pkg-\")), \"node_modules\", \"llmproxy\");\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.mkdirSync(packageRoot, { recursive: true });\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"linux\",\n stdout,\n stderr,\n env: { LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr\\n\", stderr: \"\" };\n }\n if (command === \"systemctl\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"systemd 255\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"compose\" && args[1] === \"version\") {\n return { status: 1, stdout: \"\", stderr: \"unknown command\\n\" };\n }\n if (command === \"docker-compose\" && args[0] === \"version\") {\n return { status: 0, stdout: \"Docker Compose version v2.29.0\\n\", stderr: \"\" };\n }\n if (command === \"docker\" && args[0] === \"info\") {\n return { status: 0, stdout: \"Server:\\n Containers: 0\\n\", stderr: \"\" };\n }\n if (command === \"sh\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/bin/llmproxy\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Installazione persistente completata/);\n assert.equal(commandCalls.some((entry) => entry.command === \"docker-compose\" && entry.args[0] === \"version\"), true);\n});\n\ntest(\"install remains an alias for install:persistent-en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-alias-pkg-\"));\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n fs.writeFileSync(composeFile, \"services:\\n llmproxy:\\n image: llmproxy:test\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"darwin\",\n stdout,\n stderr,\n commandRunner(command, args, spawnOptions) {\n commandCalls.push({ command, args, spawnOptions });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: \"/usr/local\\n\", stderr: \"\" };\n }\n return {\n status: 0,\n stdout: \"__LLMPROXY_GLOBAL_BIN__=/usr/local/bin/llmproxy\\n\",\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n const installCall = commandCalls.find((entry) => entry.command === \"sh\");\n assert.ok(installCall);\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Global binary: \\/usr\\/local\\/bin\\/llmproxy/);\n assert.match(stdout.toString(), /Persistent service enabled with launchd/);\n});\n\ntest(\"install alias succeeds on Windows in English\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-install-english-win-pkg-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const commandCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"install\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n platform: \"win32\",\n stdout,\n stderr,\n commandRunner(command, args) {\n commandCalls.push({ command, args });\n if (command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\") {\n return { status: 0, stdout: `${packageRoot}`, stderr: \"\" };\n }\n if (command === \"powershell.exe\") {\n return { status: 0, stdout: \"__LLMPROXY_GLOBAL_BIN__=C:\\\\Users\\\\test\\\\AppData\\\\Roaming\\\\npm\\\\llmproxy\\n__LLMPROXY_BIN_SCOPE__=user\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stderr.toString(), \"\");\n assert.match(stdout.toString(), /Persistent installation completed/);\n assert.match(stdout.toString(), /Persistent service enabled/);\n});\n\ntest(\"claude:setup rejects model names and requires a numeric index\", async () => {\n const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-model-name-rejected-\"));\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-runtime-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"claude:setup\", \"--model\", \"gpt-4.1\"], {\n cwd: tempRoot,\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Usa l'indice numerico di `llmproxy models:list`/);\n});\n\ntest(\"version prints the current package version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"help prints a short description for each command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy install:persistent-it\\s+\\[llmp in:it\\]\\s+installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /llmproxy install:persistent-en\\s+\\[llmp in:en\\]\\s+installs the current CLI globally/i);\n assert.match(stdout.toString(), /llmproxy stop\\s+\\[llmp sto\\]\\s+ferma solo l'istanza locale\\/dev/i);\n assert.match(stdout.toString(), /llmproxy test\\s+\\[llmp t\\]\\s+esegue un test rapido di inferenza contro il proxy locale/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy login\\s+\\[llmp li\\]/i);\n assert.doesNotMatch(stdout.toString(), /llmproxy logout\\s+\\[llmp lo\\]/i);\n assert.match(stdout.toString(), /llmproxy stats\\s+\\[llmp sa\\]\\s+mostra statistiche aggregate di utilizzo per provider e modello/i);\n assert.match(stdout.toString(), /llmproxy provider:available\\s+\\[llmp p:av\\]\\s+elenca i provider supportati dalla CLI/i);\n assert.match(stdout.toString(), /llmproxy update\\s+\\[llmp up\\]\\s+scarica e installa l'ultima versione/i);\n assert.match(stdout.toString(), /llmproxy uninstall\\s+\\[llmp un\\]\\s+disinstallazione completa/i);\n assert.match(stdout.toString(), /llmproxy version\\s+\\[llmp v\\]\\s+mostra la versione corrente/i);\n assert.match(stdout.toString(), /Problemi comuni:/);\n});\n\ntest(\"stop terminates only the dev foreground instance on port 5045\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-dev-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const killed = [];\n\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\"), JSON.stringify({\n pid: 42424,\n host: \"127.0.0.1\",\n port: 5045,\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return {\n status: 0,\n stdout: [\"p42424\", \"cnode\", \"n127.0.0.1:5045\", \"\"].join(\"\\n\"),\n stderr: \"\",\n };\n },\n killProcess(pid, signal) {\n killed.push({ pid, signal });\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(killed, [{ pid: 42424, signal: \"SIGTERM\" }]);\n assert.match(stdout.toString(), /Istanza dev fermata su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n assert.equal(fs.existsSync(path.join(runtimeRoot, \"service\", \"foreground-run.json\")), false);\n});\n\ntest(\"stop does not touch the persistent service and reports when no dev instance is running\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stop-none-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const serviceCalls = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stop\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n execCommand() {\n return { status: 1, stdout: \"\", stderr: \"\" };\n },\n serviceManager: {\n kind: \"launchd\",\n stop() {\n serviceCalls.push(\"stop\");\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(serviceCalls, []);\n assert.match(stdout.toString(), /Nessuna istanza dev attiva su http:\\/\\/127\\.0\\.0\\.1:5045/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"test probes every configured provider with --all-providers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-all-providers-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"copilot\", {\n access_token: \"token-copilot\",\n token_type: \"bearer\",\n provider: \"copilot\",\n auth_type: \"oauth\",\n default_model: \"gpt-5.4\",\n }, { name: \"Copilot\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.5\",\n }, { name: \"Kimi\" });\n\n const requestBodies = [];\n const fetchFn = async (_url, options = {}) => {\n const body = JSON.parse(String(options.body || \"{}\"));\n requestBodies.push(body);\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [{ type: \"text\", text: `ok ${body.provider}` }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(requestBodies.filter((body) => body.model).map((body) => [body.provider, body.model]), [[\"copilot\", \"gpt-5.4\"], [\"kimi\", \"kimi-k2.5\"]]);\n assert.match(stdout.toString(), /copilot: ok \\(gpt-5\\.4\\)/);\n assert.match(stdout.toString(), /kimi: ok \\(kimi-k2\\.5\\)/);\n assert.doesNotMatch(stdout.toString(), /ok copilot|ok kimi/);\n});\n\ntest(\"test --all-providers reports ok when response has no text content but request summary shows success\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-metadata-only-provider-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n // Simula il proxy: risposta con solo metadata e tool_use, nessun testo utile\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-metadata-only\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true, status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [\n { type: \"text\", text: \"[llmproxy] provider: opencode | model: deepseek-v4-flash-free\\n\\n\" },\n { type: \"tool_use\", id: \"toolu_1\", name: \"bash\", input: { command: \"echo hello\" } },\n ],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n});\n\ntest(\"test --all-providers waits for a delayed per-provider request summary before declaring empty response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-delayed-provider-summary-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode Zen\" });\n\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n let proxyRequestIndex = 0;\n let delayedSummaryWritten = false;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true, status: 200,\n async json() { return { ok: true, manifest_version: \"v11\" }; },\n };\n }\n\n proxyRequestIndex += 1;\n const body = JSON.parse(String(options.body || \"{}\"));\n if (proxyRequestIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-flash\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-flash\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: body.model,\n content: [\n { type: \"text\", text: `[llmproxy] provider: ${body.provider} | model: ${body.model}\\n\\n` },\n ],\n };\n },\n };\n };\n\n let sleepCalls = 0;\n const sleep = async () => {\n sleepCalls += 1;\n if (!delayedSummaryWritten && sleepCalls >= 12) {\n delayedSummaryWritten = true;\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n }\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n tokenStore,\n fetchFn,\n sleep,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /deepseek: ok \\(deepseek-v4-flash\\)/);\n assert.match(stdout.toString(), /opencode: ok \\(deepseek-v4-flash-free\\)/);\n assert.doesNotMatch(stdout.toString(), /fail risposta vuota/);\n assert.equal(delayedSummaryWritten, true);\n});\n\ntest(\"test -i runs a real inference through fallback order and prints final provider plus response\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const requests = [];\n const fetchFn = async (url, options = {}) => {\n requests.push({ url, options });\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(requests.length, 2);\n const body = JSON.parse(String(requests[1].options.body || \"{}\"));\n assert.equal(body.provider, undefined);\n assert.equal(body.model, undefined);\n assert.equal(body.messages[0].content[0].text, \"Rispondi solo: llmproxy-test-inference\");\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n assert.doesNotMatch(stdout.toString(), /^fallback:/m);\n});\n\ntest(\"test -i treats metadata-only inline output as a successful inference when the request summary is valid\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-metadata-only-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n\n const fetchFn = async (url) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-metadata-only\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{\n type: \"text\",\n text: \"[llmproxy] provider: kimi | model: kimi-k2.7-code\\n\\n[llmproxy] tokens: req 357 (in 101, out 256) | provider today 2196 week 3422 | model today 2196 week 3422\",\n }],\n };\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(kimi \\| kimi-k2\\.7-code\\)/);\n assert.doesNotMatch(stdout.toString(), /^response:/m);\n});\n\ntest(\"test -i --all-providers prints only validated working fallbacks after the winner\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-all-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"deepseek-v4-flash\", {\n access_token: \"token-deepseek-flash\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash\",\n }, { name: \"DeepSeek Flash\" });\n tokenStore.saveProvider(\"openrouter\", {\n access_token: \"token-openrouter\",\n token_type: \"api_key\",\n provider: \"openrouter\",\n auth_type: \"api_key\",\n default_model: \"minimax/minimax-m3-20260531\",\n }, { name: \"OpenRouter\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"opencode\", {\n access_token: \"token-opencode\",\n token_type: \"api_key\",\n provider: \"opencode\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-flash-free\",\n }, { name: \"OpenCode\" });\n\n let probeIndex = 0;\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n probeIndex += 1;\n\n if (probeIndex === 1) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-all\",\n success: true,\n finalProvider: \"openrouter\",\n finalModel: \"minimax/minimax-m3-20260531\",\n providerSequence: [\n { provider: \"deepseek\", status: 402, success: false, effective_model: \"deepseek-v4-pro\", actual_model: null },\n { provider: \"deepseek-v4-flash\", status: 402, success: false, effective_model: \"deepseek-v4-flash\", actual_model: null },\n { provider: \"openrouter\", status: 200, success: true, effective_model: \"minimax/minimax-m3-20260531\", actual_model: \"minimax/minimax-m3-20260531\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"minimax/minimax-m3-20260531\",\n content: [{ type: \"text\", text: \"llmproxy-test-openrouter\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-qwen-fallback\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"opencode\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-opencode-fallback\",\n success: true,\n finalProvider: \"opencode\",\n finalModel: \"deepseek-v4-flash-free\",\n providerSequence: [\n { provider: \"opencode\", status: 200, success: true, effective_model: \"deepseek-v4-flash-free\", actual_model: \"deepseek-v4-flash-free\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-flash-free\",\n content: [{ type: \"text\", text: \"llmproxy-test-opencode\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /inference: ok \\(openrouter \\| minimax\\/minimax-m3-20260531\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: opencode \\| deepseek-v4-flash-free/);\n assert.doesNotMatch(stdout.toString(), /^invalid fallback:/m);\n assert.match(stdout.toString(), /response: llmproxy-test-openrouter/);\n});\n\ntest(\"test -i --all-providers skips broken remaining fallbacks and fails the command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-test-inference-broken-remaining-\"));\n const stdout = createWritableBuffer();\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n tokenStore.saveProvider(\"deepseek\", {\n access_token: \"token-deepseek\",\n token_type: \"api_key\",\n provider: \"deepseek\",\n auth_type: \"api_key\",\n default_model: \"deepseek-v4-pro\",\n }, { name: \"DeepSeek\" });\n tokenStore.saveProvider(\"commandcode\", {\n access_token: \"token-commandcode\",\n token_type: \"api_key\",\n provider: \"commandcode\",\n auth_type: \"api_key\",\n default_model: \"Qwen/Qwen3.7-Max\",\n }, { name: \"CommandCode\" });\n tokenStore.saveProvider(\"qwen\", {\n access_token: \"token-qwen\",\n token_type: \"api_key\",\n provider: \"qwen\",\n auth_type: \"api_key\",\n default_model: \"qwen3.7-plus\",\n }, { name: \"Qwen\" });\n tokenStore.saveProvider(\"kimi\", {\n access_token: \"token-kimi\",\n token_type: \"api_key\",\n provider: \"kimi\",\n auth_type: \"api_key\",\n default_model: \"kimi-k2.7-code\",\n }, { name: \"Kimi\" });\n\n const fetchFn = async (url, options = {}) => {\n if (String(url).endsWith(\"/health\")) {\n return {\n ok: true,\n status: 200,\n async json() {\n return { ok: true, manifest_version: \"v11\" };\n },\n };\n }\n const body = JSON.parse(String(options.body || \"{}\"));\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n const logFile = path.join(logsDir, `requests-${new Date().toISOString().slice(0, 10)}.jsonl`);\n\n if (!body.provider) {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-inference-deepseek\",\n success: true,\n finalProvider: \"deepseek\",\n finalModel: \"deepseek-v4-pro\",\n providerSequence: [\n { provider: \"deepseek\", status: 200, success: true, effective_model: \"deepseek-v4-pro\", actual_model: \"deepseek-v4-pro\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"deepseek-v4-pro\",\n content: [{ type: \"text\", text: \"llmproxy-test-deepseek\" }],\n };\n },\n };\n }\n\n if (body.provider === \"qwen\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-qwen\",\n success: true,\n finalProvider: \"qwen\",\n finalModel: \"qwen3.7-plus\",\n providerSequence: [\n { provider: \"qwen\", status: 200, success: true, effective_model: \"qwen3.7-plus\", actual_model: \"qwen3.7-plus\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"qwen3.7-plus\",\n content: [{ type: \"text\", text: \"llmproxy-test-qwen\" }],\n };\n },\n };\n }\n\n if (body.provider === \"kimi\") {\n fs.appendFileSync(logFile, JSON.stringify({\n ts: new Date().toISOString(),\n event: \"request_summary\",\n requestId: \"req-fallback-kimi\",\n success: true,\n finalProvider: \"kimi\",\n finalModel: \"kimi-k2.7-code\",\n providerSequence: [\n { provider: \"kimi\", status: 200, success: true, effective_model: \"kimi-k2.7-code\", actual_model: \"kimi-k2.7-code\" },\n ],\n }) + \"\\n\", \"utf8\");\n return {\n ok: true,\n status: 200,\n async json() {\n return {\n type: \"message\",\n role: \"assistant\",\n model: \"kimi-k2.7-code\",\n content: [{ type: \"text\", text: \"llmproxy-test-kimi\" }],\n };\n },\n };\n }\n\n return {\n ok: false,\n status: 400,\n async json() {\n return {};\n },\n };\n };\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"test\", \"-i\", \"--all-providers\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n assert.equal(exitCode, 1);\n assert.match(stdout.toString(), /inference: ok \\(deepseek \\| deepseek-v4-pro\\)/);\n assert.match(stdout.toString(), /1st fallback: qwen \\| qwen3\\.7-plus/);\n assert.match(stdout.toString(), /2nd fallback: kimi \\| kimi-k2\\.7-code/);\n assert.match(stdout.toString(), /invalid fallback: commandcode -> HTTP 400 \\(Qwen\\/Qwen3\\.7-Max\\)/);\n assert.match(stdout.toString(), /response: llmproxy-test-deepseek/);\n});\n\ntest(\"stats prints provider and model token breakdown from local metering data\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-stats-\"));\n const stdout = createWritableBuffer();\n const logsDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(logsDir, { recursive: true });\n fs.writeFileSync(path.join(logsDir, \"metering.jsonl\"), [\n JSON.stringify({ timestamp: \"2026-05-01T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: true, tokens_input: 100, tokens_output: 20 }),\n JSON.stringify({ timestamp: \"2026-05-02T10:00:00Z\", provider: \"deepseek\", model_used: \"deepseek-v4-pro\", success: true, tokens_input: 50, tokens_output: 10 }),\n JSON.stringify({ timestamp: \"2026-05-03T10:00:00Z\", provider: \"qwen\", model_used: \"qwen3.7-max\", success: false, tokens_input: 25, tokens_output: 0 }),\n ].join(\"\\n\") + \"\\n\", \"utf8\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: {\n LLMPROXY_MODE: \"standalone\",\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Requests: 3 \\| Success: 2 \\| Errors: 1/);\n assert.match(stdout.toString(), /Tokens: total=205 in=175 out=30/);\n assert.match(stdout.toString(), /Providers:/);\n assert.match(stdout.toString(), /1\\. qwen requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek requests=1 total=60 in=50 out=10/);\n assert.match(stdout.toString(), /Models:/);\n assert.match(stdout.toString(), /1\\. qwen3\\.7-max requests=2 total=145 in=125 out=20/);\n assert.match(stdout.toString(), /2\\. deepseek-v4-pro requests=1 total=60 in=50 out=10/);\n});\n\ntest(\"help stats prints detailed guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-stats-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"stats\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy stats/m);\n assert.match(stdout.toString(), /breakdown per provider e modello/i);\n});\n\ntest(\"package scripts expose install:persistent-it and install:persistent-en\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.equal(pkg.scripts[\"install:persistent-it\"], \"node bin/llmproxy.js install:persistent-it\");\n assert.equal(pkg.scripts[\"install:persistent-en\"], \"node bin/llmproxy.js install:persistent-en\");\n});\n\ntest(\"package files include the Docker runtime assets for production service mode\", async () => {\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n assert.ok(pkg.files.includes(\"Dockerfile\"));\n assert.ok(pkg.files.includes(\"docker-compose.production.yml\"));\n});\n\ntest(\"help <command> prints detailed guidance for a specific command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-command-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"claude:setup\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /llmproxy claude:setup \\[--model <indice>\\]/);\n assert.match(stdout.toString(), /Quando usarlo:/);\n assert.match(stdout.toString(), /Scrive \\.claude\\/settings\\.json/);\n assert.match(stdout.toString(), /Esempio:/);\n assert.match(stdout.toString(), /llmproxy claude:setup --model 2/);\n});\n\ntest(\"help model:set prints detailed guidance for the model switch command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-model-set-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"model:set\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy model:set <model>/m);\n assert.match(stdout.toString(), /Aggiorna il modello Claude del progetto/);\n assert.match(stdout.toString(), /deepseek:deepseek-v4-flash/);\n});\n\ntest(\"help covers release-notes and provider subcommands\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-provider-commands-\"));\n const commands = [\n [\"release-notes\", /changelog/i],\n [\"provider:add\", /aggiunge un provider noto/i],\n [\"provider:key\", /API key/i],\n [\"provider:available\", /provider supportati dalla CLI/i],\n [\"provider:list\", /provider configurati/i],\n [\"provider:status\", /provider attivo/i],\n [\"provider:order\", /posizione di fallback desiderata/i],\n [\"provider:rename\", /rinomina un provider/i],\n [\"provider:remove\", /rimuove un provider/i],\n ];\n\n for (const [command, expectedPattern] of commands) {\n const stdout = createWritableBuffer();\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", command], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0, `help ${command} should succeed`);\n assert.match(stdout.toString(), expectedPattern, `help ${command} should mention its purpose`);\n }\n});\n\ntest(\"help install prints English guidance for the English install command\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-english-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install/m);\n assert.match(stdout.toString(), /Description: English alias for install:persistent/i);\n assert.match(stdout.toString(), /When to use: Use it when you want a shorter, English-first command/i);\n assert.match(stdout.toString(), /Example: llmproxy install/i);\n});\n\ntest(\"help install:persistent-en prints English guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-en\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-en/m);\n assert.match(stdout.toString(), /Description: Installs the current CLI globally/i);\n assert.match(stdout.toString(), /When to use: Use it when you want the explicit English install path/i);\n});\n\ntest(\"help install:persistent-it prints Italian guidance\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-install-persistent-it-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"help\", \"install:persistent-it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /^llmproxy install:persistent-it/m);\n assert.match(stdout.toString(), /Descrizione: Installa globalmente la CLI corrente/i);\n assert.match(stdout.toString(), /Quando usarlo: Usalo come percorso esplicito in italiano/i);\n});\n\ntest(\"--help is an alias for help\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-help-alias-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--help\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Comandi principali:/);\n});\n\ntest(\"--version is an alias for version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-version-alias-\"));\n const stdout = createWritableBuffer();\n const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"--version\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.equal(stdout.toString(), `${pkg.version}\\n`);\n});\n\ntest(\"update runs the package manager command for the latest llmproxy release\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-\"));\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"git\", \"pnpm\"].includes(command)) {\n return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"--version\") {\n return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n }\n if (command === \"npm\" && args[0] === \"prefix\") {\n return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n }\n if (command === \"sudo\") {\n return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n }\n return { status: 0, stdout: \"changed 69 packages in 3s\\n__LLMPROXY_VERSION__=0.1.0\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.deepEqual(executed.slice(0, 3), [\n [\"git\", [\"--version\"]],\n [\"npm\", [\"--version\"]],\n [\"npm\", [\"prefix\", \"-g\"]],\n ]);\n assert.ok(executed.some(([command, args]) => command === \"npm\" && args[0] === \"prefix\" && args[1] === \"-g\"));\n assert.equal(executed.at(-1)[0], \"bash\");\n assert.equal(executed.at(-1)[1][0], \"-c\");\n const scriptText = executed.at(-1)[1][1];\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /original_global_bin_path=\"\\$original_npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /npm install -g --force \"\\$package_file\"/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /for preserved_bin in \\$preserved_bin_paths; do/);\n assert.match(scriptText, /ensure_wrapper_path \"\\$preserved_bin\" >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /done/);\n assert.match(scriptText, /if \\[ -n \"\\$installed_bin\" \\] && \\[ \"\\$installed_bin\" != \"\\$new_bin\" \\] && ! is_preserved_bin_path \"\\$installed_bin\"; then/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.1\\.0/);\n});\n\ntest(\"update stops early and reports missing base prerequisites\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-missing-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-missing-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"development\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if (command === \"git\") return { status: 1, stdout: \"\", stderr: \"git missing\" };\n if (command === \"pnpm\") return { status: 1, stdout: \"\", stderr: \"pnpm missing\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"sudo\") return { status: 0, stdout: \"sudo 1.0\\n\", stderr: \"\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /prerequisiti.*non sono soddisfatti/i);\n assert.match(stderr.toString(), /Git \\(`git`\\) non trovato/i);\n assert.doesNotMatch(stderr.toString(), /pnpm non trovato/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"update reports Docker prerequisites when the service runtime uses Docker\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-preflight-docker-\"));\n const npmPrefix = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-prefix-docker-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n env: { ...process.env, LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner(command, args) {\n executed.push([command, args]);\n if ([\"gh\", \"git\", \"pnpm\"].includes(command)) return { status: 0, stdout: \"ok\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"--version\") return { status: 0, stdout: \"10.0.0\\n\", stderr: \"\" };\n if (command === \"npm\" && args[0] === \"prefix\") return { status: 0, stdout: `${npmPrefix}\\n`, stderr: \"\" };\n if (command === \"docker\") return { status: 1, stdout: \"\", stderr: \"docker missing\" };\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Docker non trovato nel PATH/i);\n assert.equal(executed.some(([command]) => command === \"bash\"), false);\n});\n\ntest(\"runSelfUpdate resolves the refreshed llmproxy binary by matching the target version\", () => {\n const executed = [];\n runSelfUpdate((command, args) => {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n });\n\n const scriptText = executed[0][1][1];\n assert.equal(typeof runSelfUpdate, \"function\");\n assert.match(scriptText, /current_bin=\\$\\(command -v llmproxy 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /current_version=\\$\\(llmproxy version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /used_sudo=0/);\n assert.match(scriptText, /original_npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /rollback_manifest=\"\\$tmpdir\\/rollback-manifest\\.txt\"/);\n assert.match(scriptText, /backup_install_dir\\(\\) \\{/);\n assert.match(scriptText, /restore_backups\\(\\) \\{/);\n assert.match(scriptText, /rollback_and_exit\\(\\) \\{/);\n assert.match(scriptText, /target_version=\\$\\(node -p \"require\\('\\.\\/package\\.json'\\)\\.version\"\\)/);\n assert.match(scriptText, /if sudo npm install -g --force \"\\$package_file\" 2>\\/dev\\/null; then\\n\\s+used_sudo=1/);\n assert.match(scriptText, /if \\[ \"\\$used_sudo\" -eq 1 \\]; then\\n\\s+npm_prefix=\\$\\(sudo npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nelse\\n\\s+npm_prefix=\\$\\(npm prefix -g 2>\\/dev\\/null \\|\\| echo \"\\/usr\\/local\"\\)\\nfi/);\n assert.match(scriptText, /install_dir=\"\\$npm_prefix\\/lib\\/node_modules\\/llmproxy\"/);\n assert.match(scriptText, /package_cli=\"\\$install_dir\\/bin\\/llmproxy\\.js\"/);\n assert.match(scriptText, /global_bin_path=\"\\$npm_prefix\\/bin\\/llmproxy\"/);\n assert.match(scriptText, /run_new_llmproxy\\(\\) \\{/);\n assert.match(scriptText, /elif \\[ -f \"\\$package_cli\" \\]; then\\n\\s+node \"\\$package_cli\" \"\\$@\"/);\n assert.match(scriptText, /build_wrapper_payload\\(\\) \\{/);\n assert.match(scriptText, /ensure_wrapper_path\\(\\) \\{/);\n assert.match(scriptText, /ensure_global_bin\\(\\) \\{/);\n assert.match(scriptText, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(scriptText, /run_new_llmproxy config:migrate >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /if \\[ -f \"\\$package_cli\" \\]; then/);\n assert.match(scriptText, /package_cli_version=\\$\\(node \"\\$package_cli\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /new_bin_mode=\"node\"/);\n assert.match(scriptText, /sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=.*DBUS_SESSION_BUS_ADDRESS=.*node \"\\$new_bin\" service:restart/);\n assert.match(scriptText, /resolved_bins=\\$\\(which -a llmproxy 2>\\/dev\\/null \\| awk '!seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /if \\[ \"\\$version_output\" != \"\\$target_version\" \\] && \\[ -x \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /global_bin_version=\\$\\(\\\"\\$global_bin_path\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /candidate_version=\\$\\(\\\"\\$candidate_bin\\\" version 2>\\/dev\\/null \\|\\| true\\)/);\n assert.match(scriptText, /ensure_global_bin >\\/dev\\/null 2>&1 \\|\\| true/);\n assert.match(scriptText, /preserved_bin_paths=\\$\\(printf \"%s\\\\n\" \"\\$current_bin\" \"\\$global_bin_path\" \"\\$original_global_bin_path\" \"\\/usr\\/bin\\/llmproxy\" \"\\/usr\\/local\\/bin\\/llmproxy\" \\$existing_bins \\| awk 'NF && !seen\\[\\$0\\]\\+\\+'\\)/);\n assert.match(scriptText, /is_preserved_bin_path\\(\\) \\{/);\n assert.match(scriptText, /if \\[ -n \"\\$current_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$new_bin\" \\] && \\[ \"\\$current_bin\" != \"\\$global_bin_path\" \\]; then/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec node \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /current_wrapper_payload=\\$\\(printf '%s\\\\n' '#!\\/bin\\/sh' \"exec \\\\\"\\$new_bin\\\\\" \\\\\"\\\\\\$@\\\\\"\" \\)/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" > \"\\$current_bin\"/);\n assert.match(scriptText, /printf \"%s\\\\n\" \"\\$current_wrapper_payload\" \\| sudo tee \"\\$current_bin\" >\\/dev\\/null/);\n assert.match(scriptText, /sudo rm -f \\\"\\$installed_bin\\\"/);\n assert.match(scriptText, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(scriptText, /post_update_check\\(\\) \\{/);\n assert.match(scriptText, /if ! post_update_check; then\\n\\s+rollback_and_exit \"post-update smoke test failed for \\$target_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK__=1\\\\n\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_VERSION__=%s\\\\n\" \"\\$restored_version\"/);\n assert.match(scriptText, /printf \"__LLMPROXY_ROLLBACK_REASON__=%s\\\\n\" \"\\$rollback_reason\"/);\n assert.match(scriptText, /release_notes_output=\\$\\(run_new_llmproxy release-notes --version \"\\$version_output\"/);\n});\n\ntest(\"buildPersistentInstallScript includes sudo-to-user D-Bus delegation on Linux\", () => {\n const installScript = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"it\",\n platform: \"linux\",\n });\n assert.match(installScript, /used_sudo=0/);\n assert.match(installScript, /if sudo npm install -g/);\n assert.match(installScript, /used_sudo=1/);\n assert.match(installScript, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installScript, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n assert.match(installScript, /if \\[ \"\\$used_sudo\" -eq 1 \\] && \\[ \"\\$platform\" = \"linux\" \\] && \\[ -n \"\\$\\{SUDO_USER:-\\}\" \\] && command -v sudo >\\/dev\\/null 2>&1; then\\n\\s+sudo -u \"\\$SUDO_USER\" XDG_RUNTIME_DIR=\"\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\" DBUS_SESSION_BUS_ADDRESS=\"unix:path=\\/run\\/user\\/\\$\\(id -u \"\\$SUDO_USER\"\\)\\/bus\" LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start/);\n assert.match(installScript, /else\\n\\s+LLMPROXY_MODE=\"standalone\" LLMPROXY_SERVICE_RUNTIME=\"native\" \"\\$global_bin\" service:start\\nfi/);\n});\n\ntest(\"buildPersistentInstallScript includes used_sudo path for update service:restart after sudo install\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /used_sudo=0/);\n assert.match(script, /if sudo npm install -g/);\n assert.match(script, /used_sudo=1/);\n assert.match(script, /sudo -u \"\\$SUDO_USER\"/);\n});\n\ntest(\"buildPersistentInstallScript persists home-local npm bins in login shell profiles\", () => {\n const script = buildPersistentInstallScript({\n packageRoot: \"/tmp/pkg\",\n locale: \"en\",\n platform: \"linux\",\n });\n assert.match(script, /append_path_export_once\\(\\) \\{/);\n assert.match(script, /persist_user_npm_bin_path\\(\\) \\{/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.bash_profile\" \"\\$bin_dir\"/);\n assert.match(script, /append_path_export_once \"\\$HOME\\/\\.zprofile\" \"\\$bin_dir\"/);\n assert.match(script, /persist_user_npm_bin_path \"\\$npm_prefix\"/);\n});\n\ntest(\"one-line installer reaps the global service port before starting llmproxy\", () => {\n const installer = fs.readFileSync(path.join(__dirname, \"..\", \"scripts\", \"install.sh\"), \"utf8\");\n assert.match(installer, /cleanup_global_service_port\\(\\) \\{/);\n assert.match(installer, /cleanup_global_service_port \"\\$\\{PORT:-7045\\}\"/);\n});\n\ntest(\"update keeps success when package install succeeds but service restart fails\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-restart-warning-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 0.2.60:\",\n \"- release 0.2.60\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"__LLMPROXY_SERVICE_RESTART_WARNING__=Bootstrap failed: 5: Input/output error\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Aggiornamento completato/);\n assert.match(stdout.toString(), /Versione corrente: 0\\.2\\.60/);\n assert.match(stdout.toString(), /Bootstrap failed: 5: Input\\/output error/);\n assert.equal(stderr.toString(), \"\");\n});\n\ntest(\"config:migrate rewrites legacy project and service variables to the current schema\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-config-migrate-\"));\n const projectRoot = path.join(runtimeRoot, \"workspace\");\n const stdout = createWritableBuffer();\n fs.mkdirSync(path.join(projectRoot, \".claude\"), { recursive: true });\n fs.writeFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), JSON.stringify({\n model: \"llmProxy\",\n env: {\n ANTHROPIC_BASE_URL: \"http://127.0.0.1:7045\",\n LLM_STATS_API_KEY: \"sk-legacy\",\n LLMPROXY_SMART_ROUTE: \"hybrid\",\n },\n }, null, 2));\n fs.mkdirSync(path.join(runtimeRoot, \"service\"), { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), JSON.stringify({\n env: {\n LLMPROXY_MONGODB_URI: \"mongodb://mongo:27017/llmProxy\",\n LLMPROXY_METERING_SINK: \"dblayer\",\n },\n }, null, 2));\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"config:migrate\"], {\n cwd: projectRoot,\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Config migration completed: project, service/);\n\n const projectPayload = JSON.parse(fs.readFileSync(path.join(projectRoot, \".claude\", \"settings.json\"), \"utf8\"));\n const servicePayload = JSON.parse(fs.readFileSync(path.join(runtimeRoot, \"service\", \"config.json\"), \"utf8\"));\n assert.equal(projectPayload.env.LLMPROXY_LLM_STATS_API_KEY, \"sk-legacy\");\n assert.equal(\"LLM_STATS_API_KEY\" in projectPayload.env, false);\n assert.equal(\"LLMPROXY_SMART_ROUTE\" in projectPayload.env, false);\n assert.equal(servicePayload.env.LLMPROXY_MONGODB_CONNECTION_STRING, \"mongodb://mongo:27017/llmProxy\");\n assert.equal(\"LLMPROXY_METERING_SINK\" in servicePayload.env, false);\n});\n\ntest(\"update reports rollback details when the installed build fails post-update verification\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-rollback-\"));\n const stdout = createWritableBuffer();\n const stderr = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n stderr,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"__LLMPROXY_ROLLBACK__=1\",\n \"__LLMPROXY_ROLLBACK_VERSION__=0.2.77\",\n \"__LLMPROXY_ROLLBACK_REASON__=post-update smoke test failed for 0.3.01\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 1);\n assert.equal(stdout.toString(), \"\");\n assert.match(stderr.toString(), /Aggiornamento fallito dopo l'installazione/);\n assert.match(stderr.toString(), /Motivo rollback: post-update smoke test failed for 0\\.3\\.01/);\n assert.match(stderr.toString(), /Versione ripristinata: 0\\.2\\.77/);\n});\n\ntest(\"update prints changelog notes for known versions\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.53\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.53:/);\n assert.match(stdout.toString(), /rebuild\\+recreate/i);\n});\n\ntest(\"update prints changelog notes for release 0.2.57\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-0257-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 10 packages in 1s\\n__LLMPROXY_VERSION__=0.2.57\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.57:/);\n assert.match(stdout.toString(), /profilo runtime esplicito/i);\n});\n\ntest(\"update exits early when the installed version already matches the online version\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-up-to-date-\"));\n const stdout = createWritableBuffer();\n const currentVersion = JSON.parse(fs.readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\")).version;\n let commandRunnerCalled = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n packageRoot: \"/Users/alessiobacin/Development/llmProxy\",\n stdout,\n fetchFn: async (url) => {\n assert.match(String(url), /raw\\.githubusercontent\\.com\\/alessiobacin\\/llmProxy\\/main\\/package\\.json/);\n return {\n ok: true,\n status: 200,\n async json() {\n return { version: currentVersion };\n },\n };\n },\n commandRunner() {\n commandRunnerCalled = true;\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(commandRunnerCalled, false);\n assert.equal(stdout.toString(), `Gia' aggiornato. Versione corrente: ${currentVersion}\\n`);\n});\n\ntest(\"release-notes prints changelog notes for release 0.2.58\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-0258-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.58\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.58:/);\n assert.match(stdout.toString(), /binario appena installato/i);\n});\n\ntest(\"release-notes formats notes from a commit message when provided\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-commit-message-\"));\n const stdout = createWritableBuffer();\n const commitMessageBase64 = Buffer.from(\"fix fallback chain\\nensure update reads release notes from git commit\\nkeep Docker rebuild in self-update\\n\", \"utf8\").toString(\"base64\");\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\", \"--commit-message-base64\", commitMessageBase64], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- fix fallback chain/);\n assert.match(stdout.toString(), /- ensure update reads release notes from git commit/);\n assert.match(stdout.toString(), /- keep Docker rebuild in self-update/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"release-notes reads embedded package notes when commit message is unavailable\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-release-notes-embedded-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"release-notes\", \"--version\", \"0.2.60\", \"--locale\", \"it\"], {\n dataRoot: runtimeRoot,\n stdout,\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.60:/);\n assert.match(stdout.toString(), /- release 0\\.2\\.60/);\n assert.match(stdout.toString(), /- embed current release notes in the installed package for first-update compatibility/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prefers release notes emitted by the newly installed binary\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-installed-binary-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return {\n status: 0,\n stdout: [\n \"changed 10 packages in 1s\",\n \"__LLMPROXY_VERSION__=9.9.9\",\n \"__LLMPROXY_RELEASE_NOTES_START__\",\n \"Changelog 9.9.9:\",\n \"- release notes from installed package\",\n \"__LLMPROXY_RELEASE_NOTES_END__\",\n \"\",\n ].join(\"\\n\"),\n stderr: \"\",\n };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /release notes from installed package/);\n assert.doesNotMatch(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"update prints changelog in English when LLMPROXY_LOCALE=en\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-en-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"en\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=0.2.54\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 0\\.2\\.54:/);\n assert.match(stdout.toString(), /Provider network: automatic retry/i);\n});\n\ntest(\"update prints fallback changelog line when release notes are missing\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-update-notes-missing-\"));\n const stdout = createWritableBuffer();\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"update\"], {\n dataRoot: runtimeRoot,\n stdout,\n env: { ...process.env, LLMPROXY_LOCALE: \"it\" },\n fetchFn: async () => ({ ok: true, status: 200, async json() { return { version: \"9.9.9\" }; } }),\n commandRunner() {\n return { status: 0, stdout: \"changed 82 packages in 2s\\n__LLMPROXY_VERSION__=9.9.9\\n\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.match(stdout.toString(), /Changelog 9\\.9\\.9:/);\n assert.match(stdout.toString(), /Note di rilascio non disponibili/);\n});\n\ntest(\"uninstall stops service, removes service file, data dir, and npm/pnpm global installs\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const dataSubDir = path.join(runtimeRoot, \"logs\");\n fs.mkdirSync(dataSubDir, { recursive: true });\n fs.writeFileSync(path.join(runtimeRoot, \"copilot-token.json\"), \"{}\");\n fs.writeFileSync(path.join(dataSubDir, \"service.out.log\"), \"log\");\n const stdout = createWritableBuffer();\n const executed = [];\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"service plist should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n const bashCall = executed.find(([cmd]) => cmd === \"bash\");\n assert.ok(bashCall, \"bash uninstall script should run\");\n assert.match(bashCall[1][1], /npm uninstall -g llmproxy/);\n assert.match(bashCall[1][1], /pnpm remove -g llmproxy/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall removes docker containers when docker managed runtime\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-docker-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const composeFile = path.join(packageRoot, \"docker-compose.production.yml\");\n fs.writeFileSync(composeFile, \"version: '3'\");\n const serviceFile = path.join(runtimeRoot, \"com.llmproxy.service.plist\");\n fs.writeFileSync(serviceFile, \"<plist></plist>\");\n const stdout = createWritableBuffer();\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"darwin\",\n env: { LLMPROXY_ENV: \"production\", LLMPROXY_SERVICE_RUNTIME: \"docker\" },\n serviceManager: {\n kind: \"launchd\",\n serviceFile,\n stop() {\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n const dockerComposeCall = executed.find(([_cmd, args]) =>\n args && args.includes(\"down\"),\n );\n assert.ok(dockerComposeCall, \"docker compose down should be called\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on linux stops systemd service and removes unit file\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-\"));\n const packageRoot = path.join(fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-linux-pkg-\")), \"node_modules\", \"llmproxy\");\n fs.mkdirSync(packageRoot, { recursive: true });\n const serviceFile = path.join(runtimeRoot, \"llmproxy.service\");\n fs.writeFileSync(serviceFile, \"[Unit]\\nDescription=test\");\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"linux\",\n serviceManager: {\n kind: \"systemd\",\n serviceFile,\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner() {\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n assert.equal(fs.existsSync(serviceFile), false, \"systemd unit file should be removed\");\n assert.equal(fs.existsSync(runtimeRoot), false, \"data root should be removed\");\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"uninstall on windows removes the service and all npm wrappers\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-\"));\n const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-uninstall-windows-pkg-\"));\n const stdout = createWritableBuffer();\n let serviceStopped = false;\n const executed = [];\n\n const exitCode = await runCli([\"node\", \"llmproxy\", \"uninstall\"], {\n dataRoot: runtimeRoot,\n packageRoot,\n stdout,\n platform: \"win32\",\n serviceManager: {\n kind: \"windows\",\n stop() {\n serviceStopped = true;\n return { ok: true, stdout: \"\", stderr: \"\" };\n },\n },\n commandRunner(command, args) {\n executed.push([command, args]);\n return { status: 0, stdout: \"\", stderr: \"\" };\n },\n });\n\n assert.equal(exitCode, 0);\n assert.equal(serviceStopped, true);\n const powershellCall = executed.find(([cmd]) => cmd === \"powershell.exe\");\n assert.ok(powershellCall, \"powershell uninstall script should run\");\n assert.match(powershellCall[1][2], /llmproxy\\.cmd/);\n assert.match(powershellCall[1][2], /llmproxy\\.ps1/);\n assert.match(powershellCall[1][2], /llmp\\.cmd/);\n assert.match(powershellCall[1][2], /llmp\\.ps1/);\n assert.match(stdout.toString(), /Disinstallazione completata/);\n});\n\ntest(\"provider:add allows multiple entries of the same provider with different models\", async () => {\n const runtimeRoot = fs.mkdtempSync(path.join(os.tmpdir(), \"llmproxy-cli-multi-model-\"));\n const stdout = createWritableBuffer();\n const fetchFn = async () => ({ ok: true, status: 200, async json() { return { id: \"ok\" }; } });\n\n // First: add deepseek-v4-flash\n const exitCode1 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-flash\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout,\n fetchFn,\n });\n\n // Second: add deepseek-v4-pro — must NOT overwrite the first\n const stdout2 = createWritableBuffer();\n const exitCode2 = await runCli([\"node\", \"llmproxy\", \"provider:add\", \"deepseek\", \"--api-key\", \"sk-ds-test\", \"--model\", \"deepseek-v4-pro\", \"--vision\", \"false\"], {\n dataRoot: runtimeRoot,\n stdout: stdout2,\n fetchFn,\n });\n\n const tokenStore = require(\"../lib/token-store\").createTokenStore({ filePath: path.join(runtimeRoot, \"copilot-token.json\") });\n const allProviders = tokenStore.listProviders();\n\n assert.equal(exitCode1, 0);\n assert.equal(exitCode2, 0);\n assert.equal(allProviders.length, 2);\n assert.equal(allProviders[0].provider, \"deepseek\");\n assert.equal(allProviders[1].provider, \"deepseek\");\n\n const models = allProviders.map((p) => p.default_model).sort();\n assert.deepEqual(models, [\"deepseek-v4-flash\", \"deepseek-v4-pro\"]);\n\n // The second entry should have a model-derived ID\n assert.match(stdout.toString(), /deepseek-v4-flash/);\n assert.match(stdout2.toString(), /deepseek-v4-pro/);\n});\n"}}}}